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