blob: 9eedae5c6c5d0a782d9cf08144d28e0219771387 [file] [log] [blame]
Andrew Geissler70d72f82022-03-23 13:15:05 -05001#include "side_switch.hpp"
2
Andrew Geissler03b30822022-03-24 11:04:40 -05003#include "utils.hpp"
4
Andrew Geissler70d72f82022-03-23 13:15:05 -05005#include <phosphor-logging/lg2.hpp>
Andrew Geissler03b30822022-03-24 11:04:40 -05006
7#include <exception>
8#include <string>
Andrew Geisslercdac8f42022-03-24 15:02:13 -05009#include <thread>
Andrew Geissler03b30822022-03-24 11:04:40 -050010#include <variant>
11#include <vector>
Andrew Geissler70d72f82022-03-23 13:15:05 -050012
13PHOSPHOR_LOG2_USING;
14
Patrick Williamsbf2bb2b2022-07-22 19:26:52 -050015bool sideSwitchNeeded(sdbusplus::bus_t& bus)
Andrew Geissler03b30822022-03-24 11:04:40 -050016{
Andrew Geisslercdac8f42022-03-24 15:02:13 -050017
Andrew Geissler03b30822022-03-24 11:04:40 -050018 std::string fwRunningVersionPath;
19 uint8_t fwRunningPriority = 0;
20
21 // Get active image
22 try
23 {
24 std::vector<std::string> paths =
25 utils::getProperty<std::vector<std::string>>(
26 bus, "/xyz/openbmc_project/software/functional",
27 "xyz.openbmc_project.Association", "endpoints");
28 if (paths.size() != 1)
29 {
30 info("side-switch only supports BMC-purpose image systems");
31 return (false);
32 }
33 fwRunningVersionPath = paths[0];
34 info("Running firmware version path is {FW_PATH}", "FW_PATH",
35 fwRunningVersionPath);
36 }
37 catch (const std::exception& e)
38 {
39 error("failed to retrieve active firmware version: {ERROR}", "ERROR",
40 e);
41 return (false);
42 }
43
44 // Check if active image has highest priority (0)
45 try
46 {
47 fwRunningPriority = utils::getProperty<uint8_t>(
48 bus, fwRunningVersionPath.c_str(),
49 "xyz.openbmc_project.Software.RedundancyPriority", "Priority");
50 info("Running firmware version priority is {FW_PRIORITY}",
51 "FW_PRIORITY", fwRunningPriority);
52 }
53 catch (const std::exception& e)
54 {
55 error("failed to read priority from active image: {ERROR}", "ERROR", e);
56 return (false);
57 }
58
59 // If running at highest priority (0) then no side switch needed
60 if (fwRunningPriority == 0)
61 {
62 info("Running image is at priority 0, no side switch needed");
63 return (false);
64 }
65
66 // Need to check if any other BMC images on system have a higher priority
67 std::vector<std::string> allSoftwarePaths;
68 try
69 {
70 auto method = bus.new_method_call("xyz.openbmc_project.ObjectMapper",
71 "/xyz/openbmc_project/object_mapper",
72 "xyz.openbmc_project.ObjectMapper",
73 "GetSubTreePaths");
74 method.append("/xyz/openbmc_project/software");
75 method.append(0); // Depth 0 to search all
76 method.append(
77 std::vector<std::string>({"xyz.openbmc_project.Software.Version"}));
78 auto reply = bus.call(method);
79 reply.read(allSoftwarePaths);
80 if (allSoftwarePaths.size() <= 1)
81 {
82 info("only 1 image present in flash so no side switch needed");
83 return (false);
84 }
85 }
86 catch (const std::exception& e)
87 {
88 error("failed to retrieve all firmware versions: {ERROR}", "ERROR", e);
89 return (false);
90 }
91
92 // Cycle through all firmware images looking for a BMC version that
93 // has a higher priority then our running image
94 for (auto& fwPath : allSoftwarePaths)
95 {
96 if (fwPath == fwRunningVersionPath)
97 {
98 info("{FW_PATH} is the running image, skip", "FW_PATH", fwPath);
99 continue;
100 }
101 try
102 {
103 uint8_t thisPathPri = utils::getProperty<uint8_t>(
104 bus, fwPath.c_str(),
105 "xyz.openbmc_project.Software.RedundancyPriority", "Priority");
106
107 if (thisPathPri < fwRunningPriority)
108 {
109 info(
110 "{FW_PATH} has a higher priority, {FW_PRIORITY}, then running priority",
111 "FW_PATH", fwPath, "FW_PRIORITY", thisPathPri);
112 return (true);
113 }
114 }
115 catch (const std::exception& e)
116 {
117 // This could just be a host firmware image, just keep going
118 info("failed to read a BMC priority from {FW_PATH}: {ERROR}",
119 "FW_PATH", fwPath, "ERROR", e);
120 continue;
121 }
122 }
123
124 return (false);
125}
126
Patrick Williamsbf2bb2b2022-07-22 19:26:52 -0500127bool powerOffSystem(sdbusplus::bus_t& bus)
Andrew Geisslercdac8f42022-03-24 15:02:13 -0500128{
129
130 try
131 {
132 utils::PropertyValue chassOff =
133 "xyz.openbmc_project.State.Chassis.Transition.Off";
134 utils::setProperty(bus, "/xyz/openbmc_project/state/chassis0",
135 "xyz.openbmc_project.State.Chassis",
136 "RequestedPowerTransition", chassOff);
137 }
138 catch (const std::exception& e)
139 {
140 error("chassis off request failed: {ERROR}", "ERROR", e);
141 return (false);
142 }
143
Andrew Geissler16ff30e2022-07-22 13:52:24 -0400144 // Now just wait for host and power to turn off
Andrew Geisslercdac8f42022-03-24 15:02:13 -0500145 // Worst case is a systemd service hangs in power off for 2 minutes so
146 // take that and double it to avoid any timing issues. The user has
147 // requested we switch to the other side, so a lengthy delay is warranted
148 // if needed. On most systems the power off takes 5-15 seconds.
149 for (int i = 0; i < 240; i++)
150 {
151 std::this_thread::sleep_for(std::chrono::milliseconds(1000));
152 try
153 {
Andrew Geissler16ff30e2022-07-22 13:52:24 -0400154 // First wait for host to be off
155 auto currentHostState = utils::getProperty<std::string>(
156 bus, "/xyz/openbmc_project/state/host0",
157 "xyz.openbmc_project.State.Host", "CurrentHostState");
158
159 if (currentHostState ==
160 "xyz.openbmc_project.State.Host.HostState.Off")
161 {
162 info("host is off");
163 }
164 else
165 {
166 continue;
167 }
168
169 // Then verify chassis power is off
Andrew Geisslercdac8f42022-03-24 15:02:13 -0500170 auto currentPwrState = utils::getProperty<std::string>(
171 bus, "/xyz/openbmc_project/state/chassis0",
172 "xyz.openbmc_project.State.Chassis", "CurrentPowerState");
173
174 if (currentPwrState ==
175 "xyz.openbmc_project.State.Chassis.PowerState.Off")
176 {
177 info("chassis power is off");
178 return (true);
179 }
Andrew Geissler16ff30e2022-07-22 13:52:24 -0400180 else
181 {
182 continue;
183 }
Andrew Geisslercdac8f42022-03-24 15:02:13 -0500184 }
185 catch (const std::exception& e)
186 {
187 error("reading chassis power state failed: {ERROR}", "ERROR", e);
188 return (false);
189 }
190 }
191 error("timeout waiting for chassis power to turn off");
192 return (false);
193}
194
Patrick Williamsbf2bb2b2022-07-22 19:26:52 -0500195bool setAutoPowerRestart(sdbusplus::bus_t& bus)
Andrew Geissler788c6a62022-03-24 15:27:24 -0500196{
197 try
198 {
199 // Set the one-time power on policy to AlwaysOn so system auto boots
200 // after BMC reboot
201 utils::PropertyValue restorePolicyOn =
202 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn";
203
204 utils::setProperty(
205 bus,
206 "/xyz/openbmc_project/control/host0/power_restore_policy/one_time",
207 "xyz.openbmc_project.Control.Power.RestorePolicy",
208 "PowerRestorePolicy", restorePolicyOn);
209 }
210 catch (const std::exception& e)
211 {
212 error("setting power policy to always on failed: {ERROR}", "ERROR", e);
213 return (false);
214 }
215 info("RestorePolicy set to AlwaysOn");
216 return (true);
217}
218
Patrick Williamsbf2bb2b2022-07-22 19:26:52 -0500219bool rebootTheBmc(sdbusplus::bus_t& bus)
Andrew Geisslera3cec432022-03-24 16:08:14 -0500220{
221 try
222 {
223 utils::PropertyValue bmcReboot =
224 "xyz.openbmc_project.State.BMC.Transition.Reboot";
225
226 utils::setProperty(bus, "/xyz/openbmc_project/state/bmc0",
227 "xyz.openbmc_project.State.BMC",
228 "RequestedBMCTransition", bmcReboot);
229 }
230 catch (const std::exception& e)
231 {
232 error("rebooting the bmc failed: {ERROR}", "ERROR", e);
233 return (false);
234 }
235 info("BMC reboot initiated");
236 return (true);
237}
238
Andrew Geissler70d72f82022-03-23 13:15:05 -0500239int main()
240{
241 info("Checking for side switch reboot");
242
Andrew Geisslercdac8f42022-03-24 15:02:13 -0500243 auto bus = sdbusplus::bus::new_default();
244
245 if (!sideSwitchNeeded(bus))
Andrew Geissler03b30822022-03-24 11:04:40 -0500246 {
247 info("Side switch not needed");
248 return 0;
249 }
250
Andrew Geisslercdac8f42022-03-24 15:02:13 -0500251 if (!powerOffSystem(bus))
252 {
253 error("unable to power off chassis");
254 return 0;
255 }
256
Andrew Geissler788c6a62022-03-24 15:27:24 -0500257 if (!setAutoPowerRestart(bus))
258 {
259 error("unable to set the auto power on restart policy");
260 // system has been powered off, best to at least continue and
261 // switch to new firmware image so continue
262 }
263
Andrew Geisslera3cec432022-03-24 16:08:14 -0500264 if (!rebootTheBmc(bus))
265 {
266 error("unable to reboot the BMC");
267 // Return invalid rc to trigger systemd target recovery and appropriate
268 // error logging
269 return -1;
270 }
271
272 return 0;
Andrew Geissler70d72f82022-03-23 13:15:05 -0500273}