xref: /openbmc/phosphor-power/phosphor-power-sequencer/src/system.cpp (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 
17 #include "system.hpp"
18 
19 #include <exception>
20 
21 namespace phosphor::power::sequencer
22 {
23 
initializeMonitoring(Services & services)24 void System::initializeMonitoring(Services& services)
25 {
26     // Initialize status monitoring in all the chassis
27     for (auto& curChassis : chassis)
28     {
29         curChassis->initializeMonitoring(services);
30     }
31     isMonitoringInitialized = true;
32 }
33 
setPowerState(PowerState newPowerState,Services & services)34 void System::setPowerState(PowerState newPowerState, Services& services)
35 {
36     verifyMonitoringInitialized();
37     verifyCanSetPowerState(newPowerState);
38 
39     // Get chassis that can be set to the new power state
40     auto chassisToSet = getChassisForNewPowerState(newPowerState, services);
41     if (chassisToSet.empty())
42     {
43         throw std::runtime_error{std::format(
44             "Unable to set system to state {}: No chassis can be set to that state",
45             PowerInterface::toString(newPowerState))};
46     }
47 
48     // Set new power state
49     powerState = newPowerState;
50 
51     // Save list of chassis selected for current power on/off attempt
52     selectedChassis = chassisToSet;
53 
54     // Set power state for selected chassis
55     for (auto& curChassis : chassis)
56     {
57         if (selectedChassis.contains(curChassis->getNumber()))
58         {
59             try
60             {
61                 curChassis->setPowerState(newPowerState, services);
62             }
63             catch (const std::exception& e)
64             {
65                 services.logErrorMsg(std::format(
66                     "Unable to set chassis {} to state {}: {}",
67                     curChassis->getNumber(),
68                     PowerInterface::toString(newPowerState), e.what()));
69             }
70         }
71     }
72 }
73 
monitor(Services & services)74 void System::monitor(Services& services)
75 {
76     verifyMonitoringInitialized();
77 
78     // Monitor the status of all chassis, including those not selected for
79     // current power on/off attempt. All chassis need to react to D-Bus status
80     // changes.
81     for (auto& curChassis : chassis)
82     {
83         try
84         {
85             curChassis->monitor(services);
86         }
87         catch (const std::exception& e)
88         {
89             services.logErrorMsg(
90                 std::format("Unable to monitor chassis {}: {}",
91                             curChassis->getNumber(), e.what()));
92         }
93     }
94 
95     // Set initial set of chassis selected for power on/off if needed
96     setInitialSelectedChassisIfNeeded();
97 
98     // Set the system power good based on the chassis power good values
99     setPowerGood();
100 
101     // Set initial system power state based on system power good if needed
102     setInitialPowerStateIfNeeded();
103 }
104 
getChassisForNewPowerState(PowerState newPowerState,Services & services)105 std::set<size_t> System::getChassisForNewPowerState(PowerState newPowerState,
106                                                     Services& services)
107 {
108     std::set<size_t> chassisForState;
109     for (auto& curChassis : chassis)
110     {
111         auto [canSet, reason] = curChassis->canSetPowerState(newPowerState);
112         if (canSet)
113         {
114             chassisForState.emplace(curChassis->getNumber());
115         }
116         else
117         {
118             services.logInfoMsg(
119                 std::format("Unable to set chassis {} to state {}: {}",
120                             curChassis->getNumber(),
121                             PowerInterface::toString(newPowerState), reason));
122         }
123     }
124     return chassisForState;
125 }
126 
setInitialSelectedChassisIfNeeded()127 void System::setInitialSelectedChassisIfNeeded()
128 {
129     if (!selectedChassis.empty())
130     {
131         // Selected set of chassis is already defined
132         return;
133     }
134 
135     // Get the set of chassis that are powered on and off. Ignore chassis with
136     // an invalid status like not present.
137     std::set<size_t> chassisOn, chassisOff;
138     for (auto& curChassis : chassis)
139     {
140         try
141         {
142             size_t chassisNumber = curChassis->getNumber();
143             if (curChassis->isPresent() && curChassis->isAvailable() &&
144                 curChassis->isInputPowerGood())
145             {
146                 if (curChassis->getPowerGood() == PowerGood::on)
147                 {
148                     chassisOn.emplace(chassisNumber);
149                 }
150                 else
151                 {
152                     chassisOff.emplace(chassisNumber);
153                 }
154             }
155         }
156         catch (...)
157         {
158             // Ignore errors; chassis status/power good might not be available
159         }
160     }
161 
162     if (chassisOn.empty())
163     {
164         // No chassis with a valid status is powered on. Assume last requested
165         // power state was off. Use powered off chassis as initial selected set.
166         selectedChassis = chassisOff;
167     }
168     else
169     {
170         // At least one chassis with a valid status is powered on. Assume last
171         // requested power state was on. Use powered on chassis as initial
172         // selected set.
173         selectedChassis = chassisOn;
174     }
175 }
176 
setPowerGood()177 void System::setPowerGood()
178 {
179     // Return if there are no chassis selected for a power on/off attempt
180     if (selectedChassis.empty())
181     {
182         return;
183     }
184 
185     // Count the number of selected chassis with power good on and off
186     size_t powerGoodOnCount{0}, powerGoodOffCount{0};
187     for (auto& curChassis : chassis)
188     {
189         if (selectedChassis.contains(curChassis->getNumber()))
190         {
191             try
192             {
193                 if (curChassis->getPowerGood() == PowerGood::on)
194                 {
195                     ++powerGoodOnCount;
196                 }
197                 else
198                 {
199                     ++powerGoodOffCount;
200                 }
201             }
202             catch (...)
203             {
204                 // Chassis power good might not be available
205             }
206         }
207     }
208 
209     if (powerGoodOnCount == selectedChassis.size())
210     {
211         // All selected chassis are on; set system power good to on
212         powerGood = PowerGood::on;
213     }
214     else if (powerGoodOffCount == selectedChassis.size())
215     {
216         // All selected chassis are off; set system power good to off
217         powerGood = PowerGood::off;
218     }
219 }
220 
setInitialPowerStateIfNeeded()221 void System::setInitialPowerStateIfNeeded()
222 {
223     if (!powerState)
224     {
225         if (powerGood)
226         {
227             if (powerGood == PowerGood::off)
228             {
229                 powerState = PowerState::off;
230             }
231             else
232             {
233                 powerState = PowerState::on;
234             }
235         }
236     }
237 }
238 
239 } // namespace phosphor::power::sequencer
240