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