10d1c3f1fSAndrew Geissler #include "config.h" 20d1c3f1fSAndrew Geissler 30d1c3f1fSAndrew Geissler #include "host_check.hpp" 40d1c3f1fSAndrew Geissler 50d1c3f1fSAndrew Geissler #include <unistd.h> 60d1c3f1fSAndrew Geissler 78ffdb269SAndrew Geissler #include <phosphor-logging/lg2.hpp> 80d1c3f1fSAndrew Geissler #include <sdbusplus/bus.hpp> 90d1c3f1fSAndrew Geissler #include <sdbusplus/exception.hpp> 109a286db2SPatrick Williams #include <xyz/openbmc_project/Condition/HostFirmware/client.hpp> 119a286db2SPatrick Williams #include <xyz/openbmc_project/ObjectMapper/client.hpp> 129a286db2SPatrick Williams #include <xyz/openbmc_project/State/Chassis/client.hpp> 130d1c3f1fSAndrew Geissler 140d1c3f1fSAndrew Geissler #include <cstdio> 150d1c3f1fSAndrew Geissler #include <cstdlib> 16*78c066f6SPatrick Williams #include <format> 170d1c3f1fSAndrew Geissler #include <fstream> 180d1c3f1fSAndrew Geissler #include <iostream> 19e9040bb8SPatrick Williams #include <ranges> 200d1c3f1fSAndrew Geissler #include <thread> 210d1c3f1fSAndrew Geissler #include <vector> 220d1c3f1fSAndrew Geissler 238ffdb269SAndrew Geissler namespace phosphor 248ffdb269SAndrew Geissler { 258ffdb269SAndrew Geissler namespace state 268ffdb269SAndrew Geissler { 278ffdb269SAndrew Geissler namespace manager 288ffdb269SAndrew Geissler { 298ffdb269SAndrew Geissler 308ffdb269SAndrew Geissler PHOSPHOR_LOG2_USING; 318ffdb269SAndrew Geissler 320d1c3f1fSAndrew Geissler using namespace std::literals; 339a286db2SPatrick Williams 349a286db2SPatrick Williams using ObjectMapper = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>; 359a286db2SPatrick Williams using Chassis = sdbusplus::client::xyz::openbmc_project::state::Chassis<>; 369a286db2SPatrick Williams using HostFirmware = 379a286db2SPatrick Williams sdbusplus::client::xyz::openbmc_project::condition::HostFirmware<>; 380d1c3f1fSAndrew Geissler 390d1c3f1fSAndrew Geissler // Required strings for sending the msg to check on host 400d1c3f1fSAndrew Geissler constexpr auto CONDITION_HOST_PROPERTY = "CurrentFirmwareCondition"; 410d1c3f1fSAndrew Geissler constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"; 420d1c3f1fSAndrew Geissler 430d1c3f1fSAndrew Geissler constexpr auto CHASSIS_STATE_SVC = "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 48f053e6feSPatrick Williams bool checkFirmwareConditionRunning(sdbusplus::bus_t& bus) 490d1c3f1fSAndrew Geissler { 500d1c3f1fSAndrew Geissler // Find all implementations of host firmware condition interface 519a286db2SPatrick Williams auto mapper = bus.new_method_call(ObjectMapper::default_service, 529a286db2SPatrick Williams ObjectMapper::instance_path, 539a286db2SPatrick Williams ObjectMapper::interface, "GetSubTree"); 540d1c3f1fSAndrew Geissler 559a286db2SPatrick Williams mapper.append("/", 0, std::vector<std::string>({HostFirmware::interface})); 560d1c3f1fSAndrew Geissler 570d1c3f1fSAndrew Geissler std::map<std::string, std::map<std::string, std::vector<std::string>>> 580d1c3f1fSAndrew Geissler mapperResponse; 590d1c3f1fSAndrew Geissler 600d1c3f1fSAndrew Geissler try 610d1c3f1fSAndrew Geissler { 620d1c3f1fSAndrew Geissler auto mapperResponseMsg = bus.call(mapper); 630d1c3f1fSAndrew Geissler mapperResponseMsg.read(mapperResponse); 640d1c3f1fSAndrew Geissler } 65f053e6feSPatrick Williams catch (const sdbusplus::exception_t& e) 660d1c3f1fSAndrew Geissler { 67ad65b2d6SAndrew Geissler error( 68ad65b2d6SAndrew Geissler "Error in mapper GetSubTree call for HostFirmware condition: {ERROR}", 698ffdb269SAndrew Geissler "ERROR", e); 700d1c3f1fSAndrew Geissler throw; 710d1c3f1fSAndrew Geissler } 720d1c3f1fSAndrew Geissler 730d1c3f1fSAndrew Geissler if (mapperResponse.empty()) 740d1c3f1fSAndrew Geissler { 758ffdb269SAndrew Geissler info("Mapper response for HostFirmware conditions is empty!"); 760d1c3f1fSAndrew Geissler return false; 770d1c3f1fSAndrew Geissler } 780d1c3f1fSAndrew Geissler 790d1c3f1fSAndrew Geissler // Now read the CurrentFirmwareCondition from all interfaces we found 800d1c3f1fSAndrew Geissler // Currently there are two implementations of this interface. One by IPMI 810d1c3f1fSAndrew Geissler // and one by PLDM. The IPMI interface does a realtime check with the host 820d1c3f1fSAndrew Geissler // when the interface is called. This means if the host is not running, 830d1c3f1fSAndrew Geissler // we will have to wait for the timeout (currently set to 3 seconds). The 840d1c3f1fSAndrew Geissler // PLDM interface reads a cached state. The PLDM service does not put itself 850d1c3f1fSAndrew Geissler // on D-Bus until it has checked with the host. Therefore it's most 860d1c3f1fSAndrew Geissler // efficient to call the PLDM interface first. Do that by going in reverse 870d1c3f1fSAndrew Geissler // of the interfaces returned to us (PLDM will be last if available) 88e9040bb8SPatrick Williams for (const auto& [path, services] : std::views::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"); 989a286db2SPatrick Williams method.append(HostFirmware::interface, CONDITION_HOST_PROPERTY); 990d1c3f1fSAndrew Geissler 1000d1c3f1fSAndrew Geissler auto response = bus.call(method); 1019a286db2SPatrick Williams std::variant<HostFirmware::FirmwareCondition> currentFwCondV; 10280db4752SNodeMan97 response.read(currentFwCondV); 10380db4752SNodeMan97 auto currentFwCond = 1049a286db2SPatrick Williams std::get<HostFirmware::FirmwareCondition>(currentFwCondV); 1050d1c3f1fSAndrew Geissler 106ef828c4bSNodeMan97 info( 107ef828c4bSNodeMan97 "Read host fw condition {COND_VALUE} from {COND_SERVICE}, {COND_PATH}", 108ef828c4bSNodeMan97 "COND_VALUE", currentFwCond, "COND_SERVICE", service, 109ef828c4bSNodeMan97 "COND_PATH", path); 110ef828c4bSNodeMan97 1119a286db2SPatrick Williams if (currentFwCond == HostFirmware::FirmwareCondition::Running) 1120d1c3f1fSAndrew Geissler { 1130d1c3f1fSAndrew Geissler return true; 1140d1c3f1fSAndrew Geissler } 1150d1c3f1fSAndrew Geissler } 116f053e6feSPatrick Williams catch (const sdbusplus::exception_t& e) 1170d1c3f1fSAndrew Geissler { 1188ffdb269SAndrew Geissler error("Error reading HostFirmware condition, error: {ERROR}, " 1198ffdb269SAndrew Geissler "service: {SERVICE} path: {PATH}", 1208ffdb269SAndrew Geissler "ERROR", e, "SERVICE", service, "PATH", path); 1210d1c3f1fSAndrew Geissler throw; 1220d1c3f1fSAndrew Geissler } 1230d1c3f1fSAndrew Geissler } 1240d1c3f1fSAndrew Geissler } 1250d1c3f1fSAndrew Geissler return false; 1260d1c3f1fSAndrew Geissler } 1270d1c3f1fSAndrew Geissler 1280d1c3f1fSAndrew Geissler // Helper function to check if chassis power is on 129f053e6feSPatrick Williams bool isChassiPowerOn(sdbusplus::bus_t& bus, size_t id) 1300d1c3f1fSAndrew Geissler { 13107e73aaaSPotin Lai auto svcname = std::string{CHASSIS_STATE_SVC} + std::to_string(id); 1329a286db2SPatrick Williams auto objpath = std::string{Chassis::namespace_path::value} + "/" + 1339a286db2SPatrick Williams std::string{Chassis::namespace_path::chassis} + 1349a286db2SPatrick Williams std::to_string(id); 13570f36d8eSPotin Lai 1360d1c3f1fSAndrew Geissler try 1370d1c3f1fSAndrew Geissler { 13870f36d8eSPotin Lai auto method = bus.new_method_call(svcname.c_str(), objpath.c_str(), 1390d1c3f1fSAndrew Geissler PROPERTY_INTERFACE, "Get"); 1409a286db2SPatrick Williams method.append(Chassis::interface, CHASSIS_STATE_POWER_PROP); 1410d1c3f1fSAndrew Geissler 1420d1c3f1fSAndrew Geissler auto response = bus.call(method); 1439a286db2SPatrick Williams std::variant<Chassis::PowerState> currentPowerStateV; 14480db4752SNodeMan97 response.read(currentPowerStateV); 145ef828c4bSNodeMan97 1469a286db2SPatrick Williams auto currentPowerState = 1479a286db2SPatrick Williams std::get<Chassis::PowerState>(currentPowerStateV); 1480d1c3f1fSAndrew Geissler 1499a286db2SPatrick Williams if (currentPowerState == Chassis::PowerState::On) 1500d1c3f1fSAndrew Geissler { 1510d1c3f1fSAndrew Geissler return true; 1520d1c3f1fSAndrew Geissler } 1530d1c3f1fSAndrew Geissler } 154f053e6feSPatrick Williams catch (const sdbusplus::exception_t& e) 1550d1c3f1fSAndrew Geissler { 1568ffdb269SAndrew Geissler error("Error reading Chassis Power State, error: {ERROR}, " 1578ffdb269SAndrew Geissler "service: {SERVICE} path: {PATH}", 15870f36d8eSPotin Lai "ERROR", e, "SERVICE", svcname.c_str(), "PATH", objpath.c_str()); 1590d1c3f1fSAndrew Geissler throw; 1600d1c3f1fSAndrew Geissler } 1610d1c3f1fSAndrew Geissler return false; 1620d1c3f1fSAndrew Geissler } 1630d1c3f1fSAndrew Geissler 16470f36d8eSPotin Lai bool isHostRunning(size_t id) 1650d1c3f1fSAndrew Geissler { 1668ffdb269SAndrew Geissler info("Check if host is running"); 1670d1c3f1fSAndrew Geissler 1680d1c3f1fSAndrew Geissler auto bus = sdbusplus::bus::new_default(); 1690d1c3f1fSAndrew Geissler 1700d1c3f1fSAndrew Geissler // No need to check if chassis power is not on 17170f36d8eSPotin Lai if (!isChassiPowerOn(bus, id)) 1720d1c3f1fSAndrew Geissler { 1738ffdb269SAndrew Geissler info("Chassis power not on, exit"); 1740d1c3f1fSAndrew Geissler return false; 1750d1c3f1fSAndrew Geissler } 1760d1c3f1fSAndrew Geissler 1770d1c3f1fSAndrew Geissler // This applications systemd service is setup to only run after all other 1780d1c3f1fSAndrew Geissler // application that could possibly implement the needed interface have 1790d1c3f1fSAndrew Geissler // been started. However, the use of mapper to find those interfaces means 1800d1c3f1fSAndrew Geissler // we have a condition where the interface may be on D-Bus but not stored 181ef828c4bSNodeMan97 // within mapper yet. There are five built in retries to check if it's 182ef828c4bSNodeMan97 // found the host is not up. This service is only called if chassis power 183ef828c4bSNodeMan97 // is on when the BMC comes up, so this wont impact most normal cases 184ef828c4bSNodeMan97 // where the BMC is rebooted with chassis power off. In cases where 185ef828c4bSNodeMan97 // chassis power is on, the host is likely running so we want to be sure 186ef828c4bSNodeMan97 // we check all interfaces 187ef828c4bSNodeMan97 for (int i = 0; i < 5; i++) 1880d1c3f1fSAndrew Geissler { 189ef828c4bSNodeMan97 debug( 190ef828c4bSNodeMan97 "Introspecting new bus objects for bus id: {ID} sleeping for 1 second.", 191ef828c4bSNodeMan97 "ID", id); 1920d1c3f1fSAndrew Geissler // Give mapper a small window to introspect new objects on bus 193ef828c4bSNodeMan97 std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 1940d1c3f1fSAndrew Geissler if (checkFirmwareConditionRunning(bus)) 1950d1c3f1fSAndrew Geissler { 1968ffdb269SAndrew Geissler info("Host is running!"); 1970d1c3f1fSAndrew Geissler // Create file for host instance and create in filesystem to 1980d1c3f1fSAndrew Geissler // indicate to services that host is running 199*78c066f6SPatrick Williams std::string hostFile = std::format(HOST_RUNNING_FILE, 0); 200*78c066f6SPatrick Williams std::ofstream outfile(hostFile); 2010d1c3f1fSAndrew Geissler outfile.close(); 2020d1c3f1fSAndrew Geissler return true; 2030d1c3f1fSAndrew Geissler } 2040d1c3f1fSAndrew Geissler } 2058ffdb269SAndrew Geissler info("Host is not running!"); 2060d1c3f1fSAndrew Geissler return false; 2070d1c3f1fSAndrew Geissler } 2088ffdb269SAndrew Geissler 2098ffdb269SAndrew Geissler } // namespace manager 2108ffdb269SAndrew Geissler } // namespace state 2118ffdb269SAndrew Geissler } // namespace phosphor 212