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