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