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