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>
1678c066f6SPatrick 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
checkFirmwareConditionRunning(sdbusplus::bus_t & bus)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
isChassiPowerOn(sdbusplus::bus_t & bus,size_t id)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
isHostRunning(size_t id)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
183*3ff5a360SManojkiran Eda // is on when the BMC comes up, so this won't 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
19978c066f6SPatrick Williams std::string hostFile = std::format(HOST_RUNNING_FILE, 0);
20078c066f6SPatrick 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