#include "side_switch.hpp" #include "utils.hpp" #include #include #include #include #include #include PHOSPHOR_LOG2_USING; bool sideSwitchNeeded(sdbusplus::bus_t& bus) { std::string fwRunningVersionPath; uint8_t fwRunningPriority = 0; // Get active image try { std::vector paths = utils::getProperty>( bus, "/xyz/openbmc_project/software/functional", "xyz.openbmc_project.Association", "endpoints"); if (paths.size() != 1) { info("side-switch only supports BMC-purpose image systems"); return (false); } fwRunningVersionPath = paths[0]; info("Running firmware version path is {FW_PATH}", "FW_PATH", fwRunningVersionPath); } catch (const std::exception& e) { error("failed to retrieve active firmware version: {ERROR}", "ERROR", e); return (false); } // Check if active image has highest priority (0) try { fwRunningPriority = utils::getProperty( bus, fwRunningVersionPath, "xyz.openbmc_project.Software.RedundancyPriority", "Priority"); info("Running firmware version priority is {FW_PRIORITY}", "FW_PRIORITY", fwRunningPriority); } catch (const std::exception& e) { error("failed to read priority from active image: {ERROR}", "ERROR", e); return (false); } // If running at highest priority (0) then no side switch needed if (fwRunningPriority == 0) { info("Running image is at priority 0, no side switch needed"); return (false); } // Need to check if any other BMC images on system have a higher priority std::vector allSoftwarePaths; try { auto method = bus.new_method_call( "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths"); method.append("/xyz/openbmc_project/software"); method.append(0); // Depth 0 to search all method.append( std::vector({"xyz.openbmc_project.Software.Version"})); auto reply = bus.call(method); reply.read(allSoftwarePaths); if (allSoftwarePaths.size() <= 1) { info("only 1 image present in flash so no side switch needed"); return (false); } } catch (const std::exception& e) { error("failed to retrieve all firmware versions: {ERROR}", "ERROR", e); return (false); } // Cycle through all firmware images looking for a BMC version that // has a higher priority then our running image for (auto& fwPath : allSoftwarePaths) { if (fwPath == fwRunningVersionPath) { info("{FW_PATH} is the running image, skip", "FW_PATH", fwPath); continue; } try { uint8_t thisPathPri = utils::getProperty( bus, fwPath, "xyz.openbmc_project.Software.RedundancyPriority", "Priority"); if (thisPathPri < fwRunningPriority) { info( "{FW_PATH} has a higher priority, {FW_PRIORITY}, then running priority", "FW_PATH", fwPath, "FW_PRIORITY", thisPathPri); return (true); } } catch (const std::exception& e) { // This could just be a host firmware image, just keep going info("failed to read a BMC priority from {FW_PATH}: {ERROR}", "FW_PATH", fwPath, "ERROR", e); continue; } } return (false); } bool powerOffSystem(sdbusplus::bus_t& bus) { try { utils::PropertyValue chassOff = "xyz.openbmc_project.State.Chassis.Transition.Off"; utils::setProperty(bus, "/xyz/openbmc_project/state/chassis0", "xyz.openbmc_project.State.Chassis", "RequestedPowerTransition", chassOff); } catch (const std::exception& e) { error("chassis off request failed: {ERROR}", "ERROR", e); return (false); } // Now just wait for host and power to turn off // Worst case is a systemd service hangs in power off for 2 minutes so // take that and double it to avoid any timing issues. The user has // requested we switch to the other side, so a lengthy delay is warranted // if needed. On most systems the power off takes 5-15 seconds. for (int i = 0; i < 240; i++) { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); try { // First wait for host to be off auto currentHostState = utils::getProperty( bus, "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host", "CurrentHostState"); if (currentHostState == "xyz.openbmc_project.State.Host.HostState.Off") { info("host is off"); } else { continue; } // Then verify chassis power is off auto currentPwrState = utils::getProperty( bus, "/xyz/openbmc_project/state/chassis0", "xyz.openbmc_project.State.Chassis", "CurrentPowerState"); if (currentPwrState == "xyz.openbmc_project.State.Chassis.PowerState.Off") { info("chassis power is off"); return (true); } else { continue; } } catch (const std::exception& e) { error("reading chassis power state failed: {ERROR}", "ERROR", e); return (false); } } error("timeout waiting for chassis power to turn off"); return (false); } bool setAutoPowerRestart(sdbusplus::bus_t& bus) { try { // Set the one-time power on policy to AlwaysOn so system auto boots // after BMC reboot utils::PropertyValue restorePolicyOn = "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn"; utils::setProperty( bus, "/xyz/openbmc_project/control/host0/power_restore_policy/one_time", "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", restorePolicyOn); } catch (const std::exception& e) { error("setting power policy to always on failed: {ERROR}", "ERROR", e); return (false); } info("RestorePolicy set to AlwaysOn"); return (true); } bool rebootTheBmc(sdbusplus::bus_t& bus) { try { utils::PropertyValue bmcReboot = "xyz.openbmc_project.State.BMC.Transition.Reboot"; utils::setProperty(bus, "/xyz/openbmc_project/state/bmc0", "xyz.openbmc_project.State.BMC", "RequestedBMCTransition", bmcReboot); } catch (const std::exception& e) { error("rebooting the bmc failed: {ERROR}", "ERROR", e); return (false); } info("BMC reboot initiated"); return (true); } int main() { info("Checking for side switch reboot"); auto bus = sdbusplus::bus::new_default(); if (!sideSwitchNeeded(bus)) { info("Side switch not needed"); return 0; } if (!powerOffSystem(bus)) { error("unable to power off chassis"); return 0; } if (!setAutoPowerRestart(bus)) { error("unable to set the auto power on restart policy"); // system has been powered off, best to at least continue and // switch to new firmware image so continue } if (!rebootTheBmc(bus)) { error("unable to reboot the BMC"); // Return invalid rc to trigger systemd target recovery and appropriate // error logging return -1; } return 0; }