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> 88ffdb269SAndrew 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> 12*80db4752SNodeMan97 #include <xyz/openbmc_project/State/Chassis/server.hpp> 130d1c3f1fSAndrew Geissler 140d1c3f1fSAndrew Geissler #include <cstdio> 150d1c3f1fSAndrew Geissler #include <cstdlib> 160d1c3f1fSAndrew Geissler #include <fstream> 170d1c3f1fSAndrew Geissler #include <iostream> 180d1c3f1fSAndrew Geissler #include <thread> 190d1c3f1fSAndrew Geissler #include <vector> 200d1c3f1fSAndrew Geissler 218ffdb269SAndrew Geissler namespace phosphor 228ffdb269SAndrew Geissler { 238ffdb269SAndrew Geissler namespace state 248ffdb269SAndrew Geissler { 258ffdb269SAndrew Geissler namespace manager 268ffdb269SAndrew Geissler { 278ffdb269SAndrew Geissler 288ffdb269SAndrew Geissler PHOSPHOR_LOG2_USING; 298ffdb269SAndrew Geissler 300d1c3f1fSAndrew Geissler using namespace std::literals; 310d1c3f1fSAndrew Geissler using namespace sdbusplus::xyz::openbmc_project::Condition::server; 320d1c3f1fSAndrew Geissler 330d1c3f1fSAndrew Geissler // Required strings for sending the msg to check on host 340d1c3f1fSAndrew Geissler constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper"; 350d1c3f1fSAndrew Geissler constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper"; 360d1c3f1fSAndrew Geissler constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper"; 370d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_INTERFACE = 380d1c3f1fSAndrew Geissler "xyz.openbmc_project.Condition.HostFirmware"; 390d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_PROPERTY = "CurrentFirmwareCondition"; 400d1c3f1fSAndrew Geissler constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 410d1c3f1fSAndrew Geissler 420d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_SVC = "xyz.openbmc_project.State.Chassis"; 4370f36d8eSPotin Lai constexpr auto CHASSIS_STATE_PATH = "/xyz/openbmc_project/state/chassis"; 440d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_INTF = "xyz.openbmc_project.State.Chassis"; 450d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_POWER_PROP = "CurrentPowerState"; 460d1c3f1fSAndrew Geissler 470d1c3f1fSAndrew Geissler // Find all implementations of Condition interface and check if host is 480d1c3f1fSAndrew Geissler // running over it 490d1c3f1fSAndrew Geissler bool checkFirmwareConditionRunning(sdbusplus::bus::bus& bus) 500d1c3f1fSAndrew Geissler { 51*80db4752SNodeMan97 using FirmwareCondition = HostFirmware::FirmwareCondition; 520d1c3f1fSAndrew Geissler // Find all implementations of host firmware condition interface 530d1c3f1fSAndrew Geissler auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 540d1c3f1fSAndrew Geissler MAPPER_INTERFACE, "GetSubTree"); 550d1c3f1fSAndrew Geissler 560d1c3f1fSAndrew Geissler mapper.append("/", 0, std::vector<std::string>({CONDITION_HOST_INTERFACE})); 570d1c3f1fSAndrew Geissler 580d1c3f1fSAndrew Geissler std::map<std::string, std::map<std::string, std::vector<std::string>>> 590d1c3f1fSAndrew Geissler mapperResponse; 600d1c3f1fSAndrew Geissler 610d1c3f1fSAndrew Geissler try 620d1c3f1fSAndrew Geissler { 630d1c3f1fSAndrew Geissler auto mapperResponseMsg = bus.call(mapper); 640d1c3f1fSAndrew Geissler mapperResponseMsg.read(mapperResponse); 650d1c3f1fSAndrew Geissler } 660a675215SPatrick Williams catch (const sdbusplus::exception::exception& e) 670d1c3f1fSAndrew Geissler { 68ad65b2d6SAndrew Geissler error( 69ad65b2d6SAndrew Geissler "Error in mapper GetSubTree call for HostFirmware condition: {ERROR}", 708ffdb269SAndrew Geissler "ERROR", e); 710d1c3f1fSAndrew Geissler throw; 720d1c3f1fSAndrew Geissler } 730d1c3f1fSAndrew Geissler 740d1c3f1fSAndrew Geissler if (mapperResponse.empty()) 750d1c3f1fSAndrew Geissler { 768ffdb269SAndrew Geissler info("Mapper response for HostFirmware conditions is empty!"); 770d1c3f1fSAndrew Geissler return false; 780d1c3f1fSAndrew Geissler } 790d1c3f1fSAndrew Geissler 800d1c3f1fSAndrew Geissler // Now read the CurrentFirmwareCondition from all interfaces we found 810d1c3f1fSAndrew Geissler // Currently there are two implementations of this interface. One by IPMI 820d1c3f1fSAndrew Geissler // and one by PLDM. The IPMI interface does a realtime check with the host 830d1c3f1fSAndrew Geissler // when the interface is called. This means if the host is not running, 840d1c3f1fSAndrew Geissler // we will have to wait for the timeout (currently set to 3 seconds). The 850d1c3f1fSAndrew Geissler // PLDM interface reads a cached state. The PLDM service does not put itself 860d1c3f1fSAndrew Geissler // on D-Bus until it has checked with the host. Therefore it's most 870d1c3f1fSAndrew Geissler // efficient to call the PLDM interface first. Do that by going in reverse 880d1c3f1fSAndrew Geissler // of the interfaces returned to us (PLDM will be last if available) 890d1c3f1fSAndrew Geissler for (const auto& [path, services] : 900d1c3f1fSAndrew Geissler boost::adaptors::reverse(mapperResponse)) 910d1c3f1fSAndrew Geissler { 920d1c3f1fSAndrew Geissler for (const auto& serviceIter : services) 930d1c3f1fSAndrew Geissler { 940d1c3f1fSAndrew Geissler const std::string& service = serviceIter.first; 950d1c3f1fSAndrew Geissler 960d1c3f1fSAndrew Geissler try 970d1c3f1fSAndrew Geissler { 980d1c3f1fSAndrew Geissler auto method = bus.new_method_call(service.c_str(), path.c_str(), 990d1c3f1fSAndrew Geissler PROPERTY_INTERFACE, "Get"); 1000d1c3f1fSAndrew Geissler method.append(CONDITION_HOST_INTERFACE, 1010d1c3f1fSAndrew Geissler CONDITION_HOST_PROPERTY); 1020d1c3f1fSAndrew Geissler 1030d1c3f1fSAndrew Geissler auto response = bus.call(method); 1040d1c3f1fSAndrew Geissler 105*80db4752SNodeMan97 std::variant<FirmwareCondition> currentFwCondV; 106*80db4752SNodeMan97 response.read(currentFwCondV); 107*80db4752SNodeMan97 auto currentFwCond = 108*80db4752SNodeMan97 std::get<FirmwareCondition>(currentFwCondV); 1090d1c3f1fSAndrew Geissler 110*80db4752SNodeMan97 if (currentFwCond == FirmwareCondition::Running) 1110d1c3f1fSAndrew Geissler { 1120d1c3f1fSAndrew Geissler return true; 1130d1c3f1fSAndrew Geissler } 1140d1c3f1fSAndrew Geissler } 1150a675215SPatrick Williams catch (const sdbusplus::exception::exception& e) 1160d1c3f1fSAndrew Geissler { 1178ffdb269SAndrew Geissler error("Error reading HostFirmware condition, error: {ERROR}, " 1188ffdb269SAndrew Geissler "service: {SERVICE} path: {PATH}", 1198ffdb269SAndrew Geissler "ERROR", e, "SERVICE", service, "PATH", path); 1200d1c3f1fSAndrew Geissler throw; 1210d1c3f1fSAndrew Geissler } 1220d1c3f1fSAndrew Geissler } 1230d1c3f1fSAndrew Geissler } 1240d1c3f1fSAndrew Geissler return false; 1250d1c3f1fSAndrew Geissler } 1260d1c3f1fSAndrew Geissler 1270d1c3f1fSAndrew Geissler // Helper function to check if chassis power is on 12870f36d8eSPotin Lai bool isChassiPowerOn(sdbusplus::bus::bus& bus, size_t id) 1290d1c3f1fSAndrew Geissler { 13007e73aaaSPotin Lai auto svcname = std::string{CHASSIS_STATE_SVC} + std::to_string(id); 13107e73aaaSPotin Lai auto objpath = std::string{CHASSIS_STATE_PATH} + std::to_string(id); 13270f36d8eSPotin Lai 1330d1c3f1fSAndrew Geissler try 1340d1c3f1fSAndrew Geissler { 135*80db4752SNodeMan97 using PowerState = 136*80db4752SNodeMan97 sdbusplus::xyz::openbmc_project::State::server::Chassis::PowerState; 13770f36d8eSPotin Lai auto method = bus.new_method_call(svcname.c_str(), objpath.c_str(), 1380d1c3f1fSAndrew Geissler PROPERTY_INTERFACE, "Get"); 1390d1c3f1fSAndrew Geissler method.append(CHASSIS_STATE_INTF, CHASSIS_STATE_POWER_PROP); 1400d1c3f1fSAndrew Geissler 1410d1c3f1fSAndrew Geissler auto response = bus.call(method); 1420d1c3f1fSAndrew Geissler 143*80db4752SNodeMan97 std::variant<PowerState> currentPowerStateV; 144*80db4752SNodeMan97 response.read(currentPowerStateV); 145*80db4752SNodeMan97 auto currentPowerState = std::get<PowerState>(currentPowerStateV); 1460d1c3f1fSAndrew Geissler 147*80db4752SNodeMan97 if (currentPowerState == PowerState::On) 1480d1c3f1fSAndrew Geissler { 1490d1c3f1fSAndrew Geissler return true; 1500d1c3f1fSAndrew Geissler } 1510d1c3f1fSAndrew Geissler } 1520a675215SPatrick Williams catch (const sdbusplus::exception::exception& e) 1530d1c3f1fSAndrew Geissler { 1548ffdb269SAndrew Geissler error("Error reading Chassis Power State, error: {ERROR}, " 1558ffdb269SAndrew Geissler "service: {SERVICE} path: {PATH}", 15670f36d8eSPotin Lai "ERROR", e, "SERVICE", svcname.c_str(), "PATH", objpath.c_str()); 1570d1c3f1fSAndrew Geissler throw; 1580d1c3f1fSAndrew Geissler } 1590d1c3f1fSAndrew Geissler return false; 1600d1c3f1fSAndrew Geissler } 1610d1c3f1fSAndrew Geissler 16270f36d8eSPotin Lai bool isHostRunning(size_t id) 1630d1c3f1fSAndrew Geissler { 1648ffdb269SAndrew Geissler info("Check if host is running"); 1650d1c3f1fSAndrew Geissler 1660d1c3f1fSAndrew Geissler auto bus = sdbusplus::bus::new_default(); 1670d1c3f1fSAndrew Geissler 1680d1c3f1fSAndrew Geissler // No need to check if chassis power is not on 16970f36d8eSPotin Lai if (!isChassiPowerOn(bus, id)) 1700d1c3f1fSAndrew Geissler { 1718ffdb269SAndrew Geissler info("Chassis power not on, exit"); 1720d1c3f1fSAndrew Geissler return false; 1730d1c3f1fSAndrew Geissler } 1740d1c3f1fSAndrew Geissler 1750d1c3f1fSAndrew Geissler // This applications systemd service is setup to only run after all other 1760d1c3f1fSAndrew Geissler // application that could possibly implement the needed interface have 1770d1c3f1fSAndrew Geissler // been started. However, the use of mapper to find those interfaces means 1780d1c3f1fSAndrew Geissler // we have a condition where the interface may be on D-Bus but not stored 1790d1c3f1fSAndrew Geissler // within mapper yet. Keep it simple and just build one retry into the 1800d1c3f1fSAndrew Geissler // check if it's found the host is not up. This service is only called if 1810d1c3f1fSAndrew Geissler // chassis power is on when the BMC comes up, so this wont impact most 1820d1c3f1fSAndrew Geissler // normal cases where the BMC is rebooted with chassis power off. In 1830d1c3f1fSAndrew Geissler // cases where chassis power is on, the host is likely running so we want 1840d1c3f1fSAndrew Geissler // to be sure we check all interfaces 1850d1c3f1fSAndrew Geissler for (int i = 0; i < 2; i++) 1860d1c3f1fSAndrew Geissler { 1870d1c3f1fSAndrew Geissler // Give mapper a small window to introspect new objects on bus 1880d1c3f1fSAndrew Geissler std::this_thread::sleep_for(std::chrono::milliseconds(500)); 1890d1c3f1fSAndrew Geissler if (checkFirmwareConditionRunning(bus)) 1900d1c3f1fSAndrew Geissler { 1918ffdb269SAndrew Geissler info("Host is running!"); 1920d1c3f1fSAndrew Geissler // Create file for host instance and create in filesystem to 1930d1c3f1fSAndrew Geissler // indicate to services that host is running 1940d1c3f1fSAndrew Geissler auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0); 1950d1c3f1fSAndrew Geissler size++; // null 1960d1c3f1fSAndrew Geissler std::unique_ptr<char[]> buf(new char[size]); 1970d1c3f1fSAndrew Geissler std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0); 1980d1c3f1fSAndrew Geissler std::ofstream outfile(buf.get()); 1990d1c3f1fSAndrew Geissler outfile.close(); 2000d1c3f1fSAndrew Geissler return true; 2010d1c3f1fSAndrew Geissler } 2020d1c3f1fSAndrew Geissler } 2038ffdb269SAndrew Geissler info("Host is not running!"); 2040d1c3f1fSAndrew Geissler return false; 2050d1c3f1fSAndrew Geissler } 2068ffdb269SAndrew Geissler 2078ffdb269SAndrew Geissler } // namespace manager 2088ffdb269SAndrew Geissler } // namespace state 2098ffdb269SAndrew Geissler } // namespace phosphor 210