xref: /openbmc/phosphor-power/phosphor-power-sequencer/src/chassis.hpp (revision b533016873041ca9287409d68f06c873ebeb0f24)
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_status_monitor.hpp"
19 #include "power_interface.hpp"
20 #include "power_sequencer_device.hpp"
21 #include "services.hpp"
22 
23 #include <stddef.h> // for size_t
24 
25 #include <format>
26 #include <memory>
27 #include <optional>
28 #include <stdexcept>
29 #include <string>
30 #include <tuple>
31 #include <utility>
32 #include <vector>
33 
34 namespace phosphor::power::sequencer
35 {
36 
37 using ChassisStatusMonitorOptions =
38     phosphor::power::util::ChassisStatusMonitorOptions;
39 using PowerState = PowerInterface::PowerState;
40 using PowerGood = PowerInterface::PowerGood;
41 
42 /**
43  * @class Chassis
44  *
45  * A chassis within the system.
46  *
47  * Chassis are typically a physical enclosure that contains system components
48  * such as CPUs, fans, power supplies, and PCIe cards. A chassis can be
49  * stand-alone, such as a tower or desktop. A chassis can also be designed to be
50  * mounted in an equipment rack.
51  */
52 class Chassis
53 {
54   public:
55     Chassis() = delete;
56     Chassis(const Chassis&) = delete;
57     Chassis(Chassis&&) = delete;
58     Chassis& operator=(const Chassis&) = delete;
59     Chassis& operator=(Chassis&&) = delete;
60     ~Chassis() = default;
61 
62     /**
63      * Constructor.
64      *
65      * @param number Chassis number within the system. Must be >= 1.
66      * @param inventoryPath D-Bus inventory path of the chassis
67      * @param powerSequencers Power sequencer devices within the chassis
68      * @param monitorOptions Types of chassis status monitoring to perform.
69      */
Chassis(size_t number,const std::string & inventoryPath,std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers,const ChassisStatusMonitorOptions & monitorOptions)70     explicit Chassis(
71         size_t number, const std::string& inventoryPath,
72         std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers,
73         const ChassisStatusMonitorOptions& monitorOptions) :
74         number{number}, inventoryPath{inventoryPath},
75         powerSequencers{std::move(powerSequencers)},
76         monitorOptions{monitorOptions}
77     {
78         // Disable monitoring for D-Bus properties owned by this application
79         this->monitorOptions.isPowerStateMonitored = false;
80         this->monitorOptions.isPowerGoodMonitored = false;
81     }
82 
83     /**
84      * Returns the chassis number within the system.
85      *
86      * @return chassis number
87      */
getNumber() const88     size_t getNumber() const
89     {
90         return number;
91     }
92 
93     /**
94      * Returns the D-Bus inventory path of the chassis.
95      *
96      * @return inventory path
97      */
getInventoryPath() const98     const std::string& getInventoryPath() const
99     {
100         return inventoryPath;
101     }
102 
103     /**
104      * Returns the power sequencer devices within the chassis.
105      *
106      * @return power sequencer devices
107      */
108     const std::vector<std::unique_ptr<PowerSequencerDevice>>&
getPowerSequencers() const109         getPowerSequencers() const
110     {
111         return powerSequencers;
112     }
113 
114     /**
115      * Returns the types of chassis status monitoring to perform.
116      *
117      * @return chassis status monitoring options
118      */
getMonitorOptions() const119     const ChassisStatusMonitorOptions& getMonitorOptions() const
120     {
121         return monitorOptions;
122     }
123 
124     /**
125      * Initializes chassis monitoring.
126      *
127      * Creates a ChassisStatusMonitor object based on the monitoring options
128      * specified in the constructor.
129      *
130      * This method must be called before any methods that return or check the
131      * chassis status.
132      *
133      * Normally this method is only called once. However, it can be called
134      * multiple times if required, such as for automated testing.
135      *
136      * @param services System services like hardware presence and the journal
137      */
initializeMonitoring(Services & services)138     void initializeMonitoring(Services& services)
139     {
140         // Note: replaces/deletes any previous monitor object
141         statusMonitor = services.createChassisStatusMonitor(
142             number, inventoryPath, monitorOptions);
143     }
144 
145     /**
146      * Returns the ChassisStatusMonitor object that is monitoring D-Bus
147      * properties for the chassis.
148      *
149      * Throws an exception if chassis monitoring has not been initialized.
150      *
151      * @return reference to ChassisStatusMonitor object
152      */
getStatusMonitor()153     ChassisStatusMonitor& getStatusMonitor()
154     {
155         verifyMonitoringInitialized();
156         return *statusMonitor;
157     }
158 
159     /**
160      * Returns whether the chassis is present.
161      *
162      * Throws an exception if:
163      * - Chassis monitoring has not been initialized
164      * - D-Bus property value could not be obtained
165      *
166      * @return true is chassis is present, false otherwise
167      */
isPresent()168     bool isPresent()
169     {
170         verifyMonitoringInitialized();
171         return statusMonitor->isPresent();
172     }
173 
174     /**
175      * Returns whether the chassis is available.
176      *
177      * If the D-Bus Available property is false, it means that communication to
178      * the chassis is not possible. For example, the chassis does not have any
179      * input power or communication cables to the BMC are disconnected.
180      *
181      * Throws an exception if:
182      * - Chassis monitoring has not been initialized
183      * - D-Bus property value could not be obtained
184      *
185      * @return true is chassis is available, false otherwise
186      */
isAvailable()187     bool isAvailable()
188     {
189         verifyMonitoringInitialized();
190         return statusMonitor->isAvailable();
191     }
192 
193     /**
194      * Returns whether the chassis is enabled.
195      *
196      * If the D-Bus Enabled property is false, it means that the chassis has
197      * been put in hardware isolation (guarded).
198      *
199      * Throws an exception if:
200      * - Chassis monitoring has not been initialized
201      * - D-Bus property value could not be obtained
202      *
203      * @return true is chassis is enabled, false otherwise
204      */
isEnabled()205     bool isEnabled()
206     {
207         verifyMonitoringInitialized();
208         return statusMonitor->isEnabled();
209     }
210 
211     /**
212      * Returns whether the chassis input power status is good.
213      *
214      * Throws an exception if:
215      * - Chassis monitoring has not been initialized
216      * - D-Bus property value could not be obtained
217      *
218      * @return true if chassis input power is good, false otherwise
219      */
isInputPowerGood()220     bool isInputPowerGood()
221     {
222         verifyMonitoringInitialized();
223         return statusMonitor->isInputPowerGood();
224     }
225 
226     /**
227      * Returns whether the power supplies power status is good.
228      *
229      * Throws an exception if:
230      * - Chassis monitoring has not been initialized
231      * - D-Bus property value could not be obtained
232      *
233      * @return true if power supplies power is good, false otherwise
234      */
isPowerSuppliesPowerGood()235     bool isPowerSuppliesPowerGood()
236     {
237         verifyMonitoringInitialized();
238         return statusMonitor->isPowerSuppliesPowerGood();
239     }
240 
241     /**
242      * Returns the last requested chassis power state.
243      *
244      * The initial power state is obtained by the monitor() method. That method
245      * must be called before calling getPowerState().
246      *
247      * Throws an exception if the power state could not be obtained.
248      *
249      * @return last requested power state
250      */
getPowerState()251     PowerState getPowerState()
252     {
253         if (!powerState)
254         {
255             throw std::runtime_error{std::format(
256                 "Power state could not be obtained for chassis {}", number)};
257         }
258         return *powerState;
259     }
260 
261     /**
262      * Returns whether the chassis can be set to the specified power state.
263      *
264      * Determined based on the current chassis status. For example, the chassis
265      * cannot be powered on if it is not present.
266      *
267      * Throws an exception if chassis monitoring has not been initialized.
268      *
269      * @param newPowerState New chassis power state
270      * @return If the state can be set, returns true and an empty string. If the
271      *         state cannot be set, returns false and a string containing the
272      *         reason.
273      */
274     std::tuple<bool, std::string> canSetPowerState(PowerState newPowerState);
275 
276     /**
277      * Sets the requested chassis power state.
278      *
279      * Powers the chassis on or off based on the specified state.
280      *
281      * Throws an exception if one of the following occurs:
282      * - Chassis monitoring has not been initialized.
283      * - Chassis D-Bus status cannot be obtained.
284      * - State change is not possible based on the chassis status.
285      * - Error occurs powering on the power sequencer devices.
286      *
287      * @param newPowerState New chassis power state
288      * @param services System services like hardware presence and the journal
289      */
290     void setPowerState(PowerState newPowerState, Services& services);
291 
292     /**
293      * Returns the chassis power good value.
294      *
295      * The power good value is read by the monitor() method. That method must be
296      * called before calling getPowerGood().
297      *
298      * Throws an exception if the power good value could not be obtained.
299      *
300      * @return chassis power good
301      */
getPowerGood()302     PowerGood getPowerGood()
303     {
304         if (!powerGood)
305         {
306             throw std::runtime_error{std::format(
307                 "Power good could not be obtained for chassis {}", number)};
308         }
309         return *powerGood;
310     }
311 
312     /**
313      * Monitors the status of the chassis.
314      *
315      * Sets the chassis power good value by reading the power good value from
316      * each power sequencer device.
317      *
318      * Reacts to any changes to chassis D-Bus properties.
319      *
320      * This method must be called periodically (such as once per second) to
321      * ensure the chassis power state and power good values are correct.
322      *
323      * Throws an exception if one of the following occurs:
324      * - Chassis monitoring has not been initialized.
325      * - Chassis D-Bus status cannot be obtained.
326      *
327      * @param services System services like hardware presence and the journal
328      */
329     void monitor(Services& services);
330 
331     /**
332      * Closes all power sequencer devices that are open.
333      *
334      * Does not throw exceptions. This method may be called because a chassis is
335      * no longer present or no longer has input power. In those scenarios
336      * closing the device may fail. However, closing the devices is still
337      * necessary in order to clean up resources like file handles.
338      */
339     void closeDevices();
340 
341   private:
342     /**
343      * Verifies that chassis monitoring has been initialized and a
344      * ChassisStatusMonitor object has been created.
345      *
346      * Throws an exception if monitoring has not been initialized.
347      */
verifyMonitoringInitialized()348     void verifyMonitoringInitialized()
349     {
350         if (!statusMonitor)
351         {
352             throw std::runtime_error{std::format(
353                 "Monitoring not initialized for chassis {}", number)};
354         }
355     }
356 
357     /**
358      * Opens the specified power sequencer device if it is not already open.
359      *
360      * Throws an exception if an error occurs.
361      *
362      * @param device power sequencer device
363      * @param services System services like hardware presence and the journal
364      */
openDeviceIfNeeded(PowerSequencerDevice & device,Services & services)365     void openDeviceIfNeeded(PowerSequencerDevice& device, Services& services)
366     {
367         if (!device.isOpen())
368         {
369             device.open(services);
370         }
371     }
372 
373     /**
374      * Reads the power good value from all power sequencer devices.
375      *
376      * Determines the combined power good value for the entire chassis.
377      *
378      * @param services System services like hardware presence and the journal
379      */
380     void readPowerGood(Services& services);
381 
382     /**
383      * Sets the initial power state value if it currently has no value.
384      *
385      * This is necessary when the application first starts or when a previously
386      * unavailable chassis becomes available.
387      *
388      * The initial power state value is based on the current power good value.
389      * We assume that the last requested power state matches the power good
390      * value. For example, if the chassis power good is on, then we assume the
391      * last requested chassis power state was on.
392      *
393      * The power state value will be set explicitly next time the chassis is
394      * powered on or off by setPowerState().
395      */
396     void setInitialPowerStateIfNeeded();
397 
398     /**
399      * Powers on all the power sequencer devices in the chassis.
400      *
401      * Throws an exception if an error occurs.
402      *
403      * @param services System services like hardware presence and the journal
404      */
405     void powerOn(Services& services);
406 
407     /**
408      * Powers off all the power sequencer devices in the chassis.
409      *
410      * Throws an exception if an error occurs.
411      *
412      * @param services System services like hardware presence and the journal
413      */
414     void powerOff(Services& services);
415 
416     /**
417      * Chassis number within the system.
418      *
419      * Chassis numbers start at 1 because chassis 0 represents the entire
420      * system.
421      */
422     size_t number;
423 
424     /**
425      * D-Bus inventory path of the chassis.
426      */
427     std::string inventoryPath{};
428 
429     /**
430      * Power sequencer devices within the chassis.
431      */
432     std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers{};
433 
434     /**
435      * Types of chassis status monitoring to perform.
436      */
437     ChassisStatusMonitorOptions monitorOptions{};
438 
439     /**
440      * Monitors the chassis status using D-Bus properties.
441      */
442     std::unique_ptr<ChassisStatusMonitor> statusMonitor{};
443 
444     /**
445      * Last requested chassis power state.
446      */
447     std::optional<PowerState> powerState{};
448 
449     /**
450      * Chassis power good.
451      */
452     std::optional<PowerGood> powerGood{};
453 };
454 
455 } // namespace phosphor::power::sequencer
456