10d1c3f1fSAndrew Geissler #include "config.h" 20d1c3f1fSAndrew Geissler 30d1c3f1fSAndrew Geissler #include "host_check.hpp" 40d1c3f1fSAndrew Geissler 50d1c3f1fSAndrew Geissler #include <unistd.h> 60d1c3f1fSAndrew Geissler 70d1c3f1fSAndrew Geissler #include <boost/range/adaptor/reversed.hpp> 8*8ffdb269SAndrew Geissler #include <phosphor-logging/lg2.hpp> 90d1c3f1fSAndrew Geissler #include <sdbusplus/bus.hpp> 100d1c3f1fSAndrew Geissler #include <sdbusplus/exception.hpp> 110d1c3f1fSAndrew Geissler #include <xyz/openbmc_project/Condition/HostFirmware/server.hpp> 120d1c3f1fSAndrew Geissler 130d1c3f1fSAndrew Geissler #include <cstdio> 140d1c3f1fSAndrew Geissler #include <cstdlib> 150d1c3f1fSAndrew Geissler #include <fstream> 160d1c3f1fSAndrew Geissler #include <iostream> 170d1c3f1fSAndrew Geissler #include <thread> 180d1c3f1fSAndrew Geissler #include <vector> 190d1c3f1fSAndrew Geissler 20*8ffdb269SAndrew Geissler namespace phosphor 21*8ffdb269SAndrew Geissler { 22*8ffdb269SAndrew Geissler namespace state 23*8ffdb269SAndrew Geissler { 24*8ffdb269SAndrew Geissler namespace manager 25*8ffdb269SAndrew Geissler { 26*8ffdb269SAndrew Geissler 27*8ffdb269SAndrew Geissler PHOSPHOR_LOG2_USING; 28*8ffdb269SAndrew Geissler 290d1c3f1fSAndrew Geissler using namespace std::literals; 300d1c3f1fSAndrew Geissler using namespace sdbusplus::xyz::openbmc_project::Condition::server; 310d1c3f1fSAndrew Geissler 320d1c3f1fSAndrew Geissler // Required strings for sending the msg to check on host 330d1c3f1fSAndrew Geissler constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 340d1c3f1fSAndrew Geissler constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 350d1c3f1fSAndrew Geissler constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; 360d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_INTERFACE = 370d1c3f1fSAndrew Geissler "xyz.openbmc_project.Condition.HostFirmware"; 380d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_PROPERTY = "CurrentFirmwareCondition"; 390d1c3f1fSAndrew Geissler constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 400d1c3f1fSAndrew Geissler 410d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_SVC = "xyz.openbmc_project.State.Chassis"; 420d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_PATH = "/xyz/openbmc_project/state/chassis0"; 430d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_INTF = "xyz.openbmc_project.State.Chassis"; 440d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_POWER_PROP = "CurrentPowerState"; 450d1c3f1fSAndrew Geissler 460d1c3f1fSAndrew Geissler // Find all implementations of Condition interface and check if host is 470d1c3f1fSAndrew Geissler // running over it 480d1c3f1fSAndrew Geissler bool checkFirmwareConditionRunning(sdbusplus::bus::bus& bus) 490d1c3f1fSAndrew Geissler { 500d1c3f1fSAndrew Geissler // Find all implementations of host firmware condition interface 510d1c3f1fSAndrew Geissler auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 520d1c3f1fSAndrew Geissler MAPPER_INTERFACE, "GetSubTree"); 530d1c3f1fSAndrew Geissler 540d1c3f1fSAndrew Geissler mapper.append("/", 0, std::vector<std::string>({CONDITION_HOST_INTERFACE})); 550d1c3f1fSAndrew Geissler 560d1c3f1fSAndrew Geissler std::map<std::string, std::map<std::string, std::vector<std::string>>> 570d1c3f1fSAndrew Geissler mapperResponse; 580d1c3f1fSAndrew Geissler 590d1c3f1fSAndrew Geissler try 600d1c3f1fSAndrew Geissler { 610d1c3f1fSAndrew Geissler auto mapperResponseMsg = bus.call(mapper); 620d1c3f1fSAndrew Geissler mapperResponseMsg.read(mapperResponse); 630d1c3f1fSAndrew Geissler } 640a675215SPatrick Williams catch (const sdbusplus::exception::exception& e) 650d1c3f1fSAndrew Geissler { 66*8ffdb269SAndrew Geissler error("Error in mapper GetSubTree call for HostFirmware condition: " 67*8ffdb269SAndrew Geissler "{ERROR}", 68*8ffdb269SAndrew Geissler "ERROR", e); 690d1c3f1fSAndrew Geissler throw; 700d1c3f1fSAndrew Geissler } 710d1c3f1fSAndrew Geissler 720d1c3f1fSAndrew Geissler if (mapperResponse.empty()) 730d1c3f1fSAndrew Geissler { 74*8ffdb269SAndrew Geissler info("Mapper response for HostFirmware conditions is empty!"); 750d1c3f1fSAndrew Geissler return false; 760d1c3f1fSAndrew Geissler } 770d1c3f1fSAndrew Geissler 780d1c3f1fSAndrew Geissler // Now read the CurrentFirmwareCondition from all interfaces we found 790d1c3f1fSAndrew Geissler // Currently there are two implementations of this interface. One by IPMI 800d1c3f1fSAndrew Geissler // and one by PLDM. The IPMI interface does a realtime check with the host 810d1c3f1fSAndrew Geissler // when the interface is called. This means if the host is not running, 820d1c3f1fSAndrew Geissler // we will have to wait for the timeout (currently set to 3 seconds). The 830d1c3f1fSAndrew Geissler // PLDM interface reads a cached state. The PLDM service does not put itself 840d1c3f1fSAndrew Geissler // on D-Bus until it has checked with the host. Therefore it's most 850d1c3f1fSAndrew Geissler // efficient to call the PLDM interface first. Do that by going in reverse 860d1c3f1fSAndrew Geissler // of the interfaces returned to us (PLDM will be last if available) 870d1c3f1fSAndrew Geissler for (const auto& [path, services] : 880d1c3f1fSAndrew Geissler boost::adaptors::reverse(mapperResponse)) 890d1c3f1fSAndrew Geissler { 900d1c3f1fSAndrew Geissler for (const auto& serviceIter : services) 910d1c3f1fSAndrew Geissler { 920d1c3f1fSAndrew Geissler const std::string& service = serviceIter.first; 930d1c3f1fSAndrew Geissler 940d1c3f1fSAndrew Geissler try 950d1c3f1fSAndrew Geissler { 960d1c3f1fSAndrew Geissler auto method = bus.new_method_call(service.c_str(), path.c_str(), 970d1c3f1fSAndrew Geissler PROPERTY_INTERFACE, "Get"); 980d1c3f1fSAndrew Geissler method.append(CONDITION_HOST_INTERFACE, 990d1c3f1fSAndrew Geissler CONDITION_HOST_PROPERTY); 1000d1c3f1fSAndrew Geissler 1010d1c3f1fSAndrew Geissler auto response = bus.call(method); 1020d1c3f1fSAndrew Geissler 1030d1c3f1fSAndrew Geissler std::variant<std::string> currentFwCond; 1040d1c3f1fSAndrew Geissler response.read(currentFwCond); 1050d1c3f1fSAndrew Geissler 1060d1c3f1fSAndrew Geissler if (std::get<std::string>(currentFwCond) == 1070d1c3f1fSAndrew Geissler "xyz.openbmc_project.Condition.HostFirmware." 1080d1c3f1fSAndrew Geissler "FirmwareCondition." 1090d1c3f1fSAndrew Geissler "Running") 1100d1c3f1fSAndrew Geissler { 1110d1c3f1fSAndrew Geissler return true; 1120d1c3f1fSAndrew Geissler } 1130d1c3f1fSAndrew Geissler } 1140a675215SPatrick Williams catch (const sdbusplus::exception::exception& e) 1150d1c3f1fSAndrew Geissler { 116*8ffdb269SAndrew Geissler error("Error reading HostFirmware condition, error: {ERROR}, " 117*8ffdb269SAndrew Geissler "service: {SERVICE} path: {PATH}", 118*8ffdb269SAndrew Geissler "ERROR", e, "SERVICE", service, "PATH", path); 1190d1c3f1fSAndrew Geissler throw; 1200d1c3f1fSAndrew Geissler } 1210d1c3f1fSAndrew Geissler } 1220d1c3f1fSAndrew Geissler } 1230d1c3f1fSAndrew Geissler return false; 1240d1c3f1fSAndrew Geissler } 1250d1c3f1fSAndrew Geissler 1260d1c3f1fSAndrew Geissler // Helper function to check if chassis power is on 1270d1c3f1fSAndrew Geissler bool isChassiPowerOn(sdbusplus::bus::bus& bus) 1280d1c3f1fSAndrew Geissler { 1290d1c3f1fSAndrew Geissler try 1300d1c3f1fSAndrew Geissler { 1310d1c3f1fSAndrew Geissler auto method = bus.new_method_call(CHASSIS_STATE_SVC, CHASSIS_STATE_PATH, 1320d1c3f1fSAndrew Geissler PROPERTY_INTERFACE, "Get"); 1330d1c3f1fSAndrew Geissler method.append(CHASSIS_STATE_INTF, CHASSIS_STATE_POWER_PROP); 1340d1c3f1fSAndrew Geissler 1350d1c3f1fSAndrew Geissler auto response = bus.call(method); 1360d1c3f1fSAndrew Geissler 1370d1c3f1fSAndrew Geissler std::variant<std::string> currentPowerState; 1380d1c3f1fSAndrew Geissler response.read(currentPowerState); 1390d1c3f1fSAndrew Geissler 1400d1c3f1fSAndrew Geissler if (std::get<std::string>(currentPowerState) == 1410d1c3f1fSAndrew Geissler "xyz.openbmc_project.State.Chassis.PowerState.On") 1420d1c3f1fSAndrew Geissler { 1430d1c3f1fSAndrew Geissler return true; 1440d1c3f1fSAndrew Geissler } 1450d1c3f1fSAndrew Geissler } 1460a675215SPatrick Williams catch (const sdbusplus::exception::exception& e) 1470d1c3f1fSAndrew Geissler { 148*8ffdb269SAndrew Geissler error("Error reading Chassis Power State, error: {ERROR}, " 149*8ffdb269SAndrew Geissler "service: {SERVICE} path: {PATH}", 150*8ffdb269SAndrew Geissler "ERROR", e, "SERVICE", CHASSIS_STATE_SVC, "PATH", 151*8ffdb269SAndrew Geissler CHASSIS_STATE_PATH); 1520d1c3f1fSAndrew Geissler throw; 1530d1c3f1fSAndrew Geissler } 1540d1c3f1fSAndrew Geissler return false; 1550d1c3f1fSAndrew Geissler } 1560d1c3f1fSAndrew Geissler 1570d1c3f1fSAndrew Geissler bool isHostRunning() 1580d1c3f1fSAndrew Geissler { 159*8ffdb269SAndrew Geissler info("Check if host is running"); 1600d1c3f1fSAndrew Geissler 1610d1c3f1fSAndrew Geissler auto bus = sdbusplus::bus::new_default(); 1620d1c3f1fSAndrew Geissler 1630d1c3f1fSAndrew Geissler // No need to check if chassis power is not on 1640d1c3f1fSAndrew Geissler if (!isChassiPowerOn(bus)) 1650d1c3f1fSAndrew Geissler { 166*8ffdb269SAndrew Geissler info("Chassis power not on, exit"); 1670d1c3f1fSAndrew Geissler return false; 1680d1c3f1fSAndrew Geissler } 1690d1c3f1fSAndrew Geissler 1700d1c3f1fSAndrew Geissler // This applications systemd service is setup to only run after all other 1710d1c3f1fSAndrew Geissler // application that could possibly implement the needed interface have 1720d1c3f1fSAndrew Geissler // been started. However, the use of mapper to find those interfaces means 1730d1c3f1fSAndrew Geissler // we have a condition where the interface may be on D-Bus but not stored 1740d1c3f1fSAndrew Geissler // within mapper yet. Keep it simple and just build one retry into the 1750d1c3f1fSAndrew Geissler // check if it's found the host is not up. This service is only called if 1760d1c3f1fSAndrew Geissler // chassis power is on when the BMC comes up, so this wont impact most 1770d1c3f1fSAndrew Geissler // normal cases where the BMC is rebooted with chassis power off. In 1780d1c3f1fSAndrew Geissler // cases where chassis power is on, the host is likely running so we want 1790d1c3f1fSAndrew Geissler // to be sure we check all interfaces 1800d1c3f1fSAndrew Geissler for (int i = 0; i < 2; i++) 1810d1c3f1fSAndrew Geissler { 1820d1c3f1fSAndrew Geissler // Give mapper a small window to introspect new objects on bus 1830d1c3f1fSAndrew Geissler std::this_thread::sleep_for(std::chrono::milliseconds(500)); 1840d1c3f1fSAndrew Geissler if (checkFirmwareConditionRunning(bus)) 1850d1c3f1fSAndrew Geissler { 186*8ffdb269SAndrew Geissler info("Host is running!"); 1870d1c3f1fSAndrew Geissler // Create file for host instance and create in filesystem to 1880d1c3f1fSAndrew Geissler // indicate to services that host is running 1890d1c3f1fSAndrew Geissler auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0); 1900d1c3f1fSAndrew Geissler size++; // null 1910d1c3f1fSAndrew Geissler std::unique_ptr<char[]> buf(new char[size]); 1920d1c3f1fSAndrew Geissler std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0); 1930d1c3f1fSAndrew Geissler std::ofstream outfile(buf.get()); 1940d1c3f1fSAndrew Geissler outfile.close(); 1950d1c3f1fSAndrew Geissler return true; 1960d1c3f1fSAndrew Geissler } 1970d1c3f1fSAndrew Geissler } 198*8ffdb269SAndrew Geissler info("Host is not running!"); 1990d1c3f1fSAndrew Geissler return false; 2000d1c3f1fSAndrew Geissler } 201*8ffdb269SAndrew Geissler 202*8ffdb269SAndrew Geissler } // namespace manager 203*8ffdb269SAndrew Geissler } // namespace state 204*8ffdb269SAndrew Geissler } // namespace phosphor 205