xref: /openbmc/phosphor-power/phosphor-power-sequencer/src/system.hpp (revision 8cf26d93db5458c17fbe64ae74156f8a31989d6b)
1 /**
2  * Copyright © 2025 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include "chassis.hpp"
19 #include "power_interface.hpp"
20 #include "services.hpp"
21 
22 #include <stddef.h> // for size_t
23 
24 #include <memory>
25 #include <optional>
26 #include <set>
27 #include <stdexcept>
28 #include <utility>
29 #include <vector>
30 
31 namespace phosphor::power::sequencer
32 {
33 
34 /**
35  * @class System
36  *
37  * The computer system being controlled and monitored by the BMC.
38  *
39  * The system contains one or more chassis.
40  */
41 class System
42 {
43   public:
44     System() = delete;
45     System(const System&) = delete;
46     System(System&&) = delete;
47     System& operator=(const System&) = delete;
48     System& operator=(System&&) = delete;
49     ~System() = default;
50 
51     /**
52      * Constructor.
53      *
54      * @param chassis Chassis in the system
55      */
System(std::vector<std::unique_ptr<Chassis>> chassis)56     explicit System(std::vector<std::unique_ptr<Chassis>> chassis) :
57         chassis{std::move(chassis)}
58     {}
59 
60     /**
61      * Returns the chassis in the system.
62      *
63      * @return chassis
64      */
getChassis() const65     const std::vector<std::unique_ptr<Chassis>>& getChassis() const
66     {
67         return chassis;
68     }
69 
70     /**
71      * Initializes system monitoring.
72      *
73      * This method must be called before any methods that return or check the
74      * system status.
75      *
76      * Normally this method is only called once. However, it can be called
77      * multiple times if required, such as for automated testing.
78      *
79      * @param services System services like hardware presence and the journal
80      */
81     void initializeMonitoring(Services& services);
82 
83     /**
84      * Returns the last requested system power state.
85      *
86      * The initial power state is obtained by the monitor() method. That method
87      * must be called before calling getPowerState().
88      *
89      * Throws an exception if the power state could not be obtained.
90      *
91      * @return last requested power state
92      */
getPowerState()93     PowerState getPowerState()
94     {
95         if (!powerState)
96         {
97             throw std::runtime_error{
98                 "System power state could not be obtained"};
99         }
100         return *powerState;
101     }
102 
103     /**
104      * Sets the requested system power state.
105      *
106      * Powers the system on or off based on the specified state.
107      *
108      * Throws an exception if one of the following occurs:
109      * - System monitoring has not been initialized
110      * - System cannot be set to the specified power state
111      * - No chassis can be set to the specified power state
112      *
113      * @param newPowerState New system power state
114      * @param services System services like hardware presence and the journal
115      */
116     void setPowerState(PowerState newPowerState, Services& services);
117 
118     /**
119      * Returns the chassis numbers selected for the current power on/off
120      * attempt.
121      *
122      * @return chassis numbers in current power on/off attempt
123      */
getSelectedChassis()124     const std::set<size_t>& getSelectedChassis()
125     {
126         return selectedChassis;
127     }
128 
129     /**
130      * Returns the system power good value.
131      *
132      * The power good value is set by the monitor() method. That method must be
133      * called before calling getPowerGood().
134      *
135      * Throws an exception if the power good value could not be obtained.
136      *
137      * @return system power good
138      */
getPowerGood()139     PowerGood getPowerGood()
140     {
141         if (!powerGood)
142         {
143             throw std::runtime_error{"System power good could not be obtained"};
144         }
145         return *powerGood;
146     }
147 
148     /**
149      * Monitors the status of the system.
150      *
151      * Sets the system power good value by obtaining the power good value from
152      * each chassis selected for the current power on/off attempt.
153      *
154      * This method must be called periodically (such as once per second) to
155      * ensure the system power state and power good values are correct.
156      *
157      * Throws an exception if system monitoring has not been initialized.
158      *
159      * @param services System services like hardware presence and the journal
160      */
161     void monitor(Services& services);
162 
163   private:
164     /**
165      * Verifies that system monitoring has been initialized.
166      *
167      * Throws an exception if monitoring has not been initialized.
168      */
verifyMonitoringInitialized()169     void verifyMonitoringInitialized()
170     {
171         if (!isMonitoringInitialized)
172         {
173             throw std::runtime_error{
174                 "System monitoring has not been initialized"};
175         }
176     }
177 
178     /**
179      * Verifies that the system can be set to the specified power state.
180      *
181      * Throws an exception if the new power state is not allowed.
182      *
183      * @param newPowerState New system power state
184      */
verifyCanSetPowerState(PowerState newPowerState)185     void verifyCanSetPowerState(PowerState newPowerState)
186     {
187         if (powerState && (powerState == newPowerState))
188         {
189             throw std::runtime_error{std::format(
190                 "Unable to set system to state {}: Already at requested state",
191                 PowerInterface::toString(newPowerState))};
192         }
193     }
194 
195     /**
196      * Returns the set of chassis numbers that can be set to the specified new
197      * power state.
198      *
199      * Throws an exception if system monitoring has not been initialized.
200      *
201      * @param newPowerState New system power state
202      * @param services System services like hardware presence and the journal
203      * @return set of chassis numbers that can be set to new power state
204      */
205     std::set<size_t> getChassisForNewPowerState(PowerState newPowerState,
206                                                 Services& services);
207 
208     /**
209      * Defines the initial set of chassis selected for power on/off if needed.
210      *
211      * This is necessary when the application first starts.
212      *
213      * We do not know which chassis were selected for power on/off prior to the
214      * application starting.
215      *
216      * Define the selected chassis set based on which chassis are currently
217      * powered on and off.
218      *
219      * The selected chassis will be set explicitly next time the system is
220      * powered on or off by setPowerState().
221      */
222     void setInitialSelectedChassisIfNeeded();
223 
224     /**
225      * Sets the system power good value based on the chassis power good values.
226      */
227     void setPowerGood();
228 
229     /**
230      * Sets the initial power state value if it currently has no value.
231      *
232      * This is necessary when the application first starts.
233      *
234      * The initial power state value is based on the current power good value.
235      * We assume that the last requested power state matches the power good
236      * value. For example, if the system power good is on, then we assume the
237      * last requested system power state was on.
238      *
239      * The power state value will be set explicitly next time the system is
240      * powered on or off by setPowerState().
241      */
242     void setInitialPowerStateIfNeeded();
243 
244     /**
245      * Chassis in the system.
246      */
247     std::vector<std::unique_ptr<Chassis>> chassis{};
248 
249     /**
250      * Indicates whether system monitoring has been initialized.
251      */
252     bool isMonitoringInitialized{false};
253 
254     /**
255      * Last requested system power state.
256      */
257     std::optional<PowerState> powerState{};
258 
259     /**
260      * System power good.
261      */
262     std::optional<PowerGood> powerGood{};
263 
264     /**
265      * Chassis numbers that were selected for the current power on/off attempt.
266      */
267     std::set<size_t> selectedChassis;
268 };
269 
270 } // namespace phosphor::power::sequencer
271