1 2 #include "absl/flags/flag.h" 3 #include "absl/flags/parse.h" 4 5 #include "host_gpio_monitor_conf.hpp" 6 7 #include <boost/asio/io_context.hpp> 8 #include <boost/asio/steady_timer.hpp> 9 #include <phosphor-logging/lg2.hpp> 10 #include <sdbusplus/asio/connection.hpp> 11 #include <sdbusplus/asio/property.hpp> 12 #include <sdbusplus/bus.hpp> 13 #include <sdbusplus/bus/match.hpp> 14 15 #include <format> 16 17 ABSL_FLAG(std::string, host_label, "0", 18 "Label for the host in question. Usually this is an integer."); 19 20 const constexpr char* OperatingSystemStateInactive = 21 "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive"; 22 const constexpr char* BareMetalActiveTargetTemplate = 23 "gbmc-bare-metal-active@{}.target"; 24 25 const constexpr char* SystemdService = "org.freedesktop.systemd1"; 26 const constexpr char* SystemdManagerObject = "/org/freedesktop/systemd1"; 27 const constexpr char* SystemdManagerInterface = 28 "org.freedesktop.systemd1.Manager"; 29 30 void setUnitStatus(sdbusplus::asio::connection& bus, bool status, 31 const std::string& host_instance) 32 { 33 auto method = bus.new_method_call(SystemdService, SystemdManagerObject, 34 SystemdManagerInterface, 35 status ? "StartUnit" : "StopUnit"); 36 method.append(std::format(BareMetalActiveTargetTemplate, host_instance), 37 "replace"); 38 39 bus.call(method); 40 } 41 42 void checkPostComplete(sdbusplus::asio::connection& bus, 43 const std::string& state, bool action, 44 const std::string& host_instance) 45 { 46 sdbusplus::asio::getProperty<std::string>( 47 bus, std::format(DBUS_SERVICE_NAME, host_instance), 48 std::format(DBUS_OBJECT_PATH, host_instance), DBUS_INTERFACE, 49 DBUS_PROPERTY_NAME, 50 [&, state, action](const boost::system::error_code& ec, 51 const std::string& postCompleteState) { 52 if (ec) 53 { 54 lg2::error("Error when checking Post Complete GPIO state"); 55 return; 56 } 57 58 lg2::info("Post Complete state is {STATE}", "STATE", 59 postCompleteState); 60 61 /* 62 * If the host OS is running (e.g. OperatingSystemState is Standby), 63 * enable the bare-metal-active systemd target. 64 * If the host CPU is in reset (e.g. OperatingSystemState is 65 * Inactive), no-op cause IPMI is enabled by default. 66 */ 67 if (postCompleteState == state) 68 { 69 setUnitStatus(bus, action, host_instance); 70 } 71 }); 72 } 73 74 /* This only gets called once on startup. */ 75 void checkPostCompleteStartup(sdbusplus::asio::connection& bus, 76 const std::string& host_instance) 77 { 78 checkPostComplete(bus, DBUS_PROPERTY_HOST_RUNNING_VALUE, true, 79 host_instance); 80 } 81 82 /* Gets called when a GPIO state change is detected. */ 83 void checkPostCompleteEvent(sdbusplus::asio::connection& bus, 84 const std::string& host_instance) 85 { 86 checkPostComplete(bus, DBUS_PROPERTY_HOST_IN_RESET_VALUE, false, 87 host_instance); 88 } 89 90 int main(int argc, char** argv) 91 { 92 absl::ParseCommandLine(argc, argv); 93 std::string host_label = absl::GetFlag(FLAGS_host_label); 94 95 try 96 { 97 /* Setup connection to dbus. */ 98 boost::asio::io_context io; 99 auto conn = sdbusplus::asio::connection(io); 100 101 /* check IPMI status at startup */ 102 checkPostCompleteStartup(conn, host_label); 103 /* 104 * Set up an event handler to process Post Complete GPIO state changes. 105 */ 106 boost::asio::steady_timer filterTimer(io); 107 108 /* 109 * Prepare object path we want to monitor, substituting in the host 110 * label, if needed. 111 */ 112 std::string objectPath = std::format(DBUS_OBJECT_PATH, host_label); 113 114 auto match = std::make_unique<sdbusplus::bus::match_t>( 115 static_cast<sdbusplus::bus_t&>(conn), 116 std::format( 117 "type='signal',member='PropertiesChanged',path_namespace='" 118 "{}',arg0namespace='{}'", 119 objectPath, DBUS_INTERFACE), 120 [&](sdbusplus::message_t& message) { 121 if (message.is_method_error()) 122 { 123 lg2::error("eventHandler callback method error"); 124 return; 125 } 126 127 /* 128 * This implicitly cancels the timer, if it's already pending. 129 * If there's a burst of events within a short period, we want 130 * to handle them all at once. So, we will wait this long for no 131 * more events to occur, before processing them. 132 */ 133 filterTimer.expires_from_now(std::chrono::seconds(1)); 134 135 filterTimer.async_wait( 136 [&](const boost::system::error_code& ec) { 137 if (ec == boost::asio::error::operation_aborted) 138 { 139 /* we were canceled */ 140 return; 141 } 142 if (ec) 143 { 144 lg2::error("timer error"); 145 return; 146 } 147 148 /* 149 * Stop the bare metal active target if the post 150 * complete got deasserted. 151 */ 152 checkPostCompleteEvent(conn, host_label); 153 }); 154 }); 155 156 io.run(); 157 return 0; 158 } 159 catch (const std::exception& e) 160 { 161 lg2::error(e.what(), "REDFISH_MESSAGE_ID", 162 std::string("OpenBMC.1.0.ServiceException")); 163 164 return 2; 165 } 166 return 1; 167 } 168