xref: /openbmc/phosphor-bmc-code-mgmt/bmc/side-switch/side_switch.cpp (revision cab87e9cdeeb3e166d6d577511f6be4dc7721aca)
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