1 #include "config.h"
2 
3 #include "utils.hpp"
4 
5 #include <gpiod.h>
6 
7 #include <phosphor-logging/lg2.hpp>
8 #include <xyz/openbmc_project/Dump/Create/client.hpp>
9 #include <xyz/openbmc_project/Logging/Create/client.hpp>
10 #include <xyz/openbmc_project/ObjectMapper/client.hpp>
11 #include <xyz/openbmc_project/Software/ActivationBlocksTransition/client.hpp>
12 #include <xyz/openbmc_project/State/BMC/client.hpp>
13 
14 #include <chrono>
15 #include <filesystem>
16 #include <format>
17 
18 namespace phosphor
19 {
20 namespace state
21 {
22 namespace manager
23 {
24 namespace utils
25 {
26 
27 using namespace std::literals::chrono_literals;
28 
29 PHOSPHOR_LOG2_USING;
30 
31 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
32 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
33 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
34 constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
35 
36 using ObjectMapper = sdbusplus::client::xyz::openbmc_project::ObjectMapper<>;
37 using ActBlockTrans = sdbusplus::client::xyz::openbmc_project::software::
38     ActivationBlocksTransition<>;
39 
subscribeToSystemdSignals(sdbusplus::bus_t & bus)40 void subscribeToSystemdSignals(sdbusplus::bus_t& bus)
41 {
42     auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
43                                       SYSTEMD_INTERFACE, "Subscribe");
44 
45     try
46     {
47         // On OpenBMC based systems, systemd has had a few situations where it
48         // has been unable to respond to this call within the default d-bus
49         // timeout of 25 seconds. This is due to the large amount of work being
50         // done by systemd during OpenBMC startup. Set the timeout for this call
51         // to 60 seconds (worst case seen was around 30s so double it).
52         bus.call(method, 60s);
53     }
54     catch (const sdbusplus::exception_t& e)
55     {
56         error("Failed to subscribe to systemd signals: {ERROR}", "ERROR", e);
57         throw std::runtime_error("Unable to subscribe to systemd signals");
58     }
59     return;
60 }
61 
getService(sdbusplus::bus_t & bus,std::string path,std::string interface)62 std::string getService(sdbusplus::bus_t& bus, std::string path,
63                        std::string interface)
64 {
65     auto mapper = bus.new_method_call(ObjectMapper::default_service,
66                                       ObjectMapper::instance_path,
67                                       ObjectMapper::interface, "GetObject");
68 
69     mapper.append(path, std::vector<std::string>({interface}));
70 
71     std::vector<std::pair<std::string, std::vector<std::string>>>
72         mapperResponse;
73 
74     try
75     {
76         auto mapperResponseMsg = bus.call(mapper);
77 
78         mapperResponseMsg.read(mapperResponse);
79         if (mapperResponse.empty())
80         {
81             error(
82                 "Error no matching service with path {PATH} and interface {INTERFACE}",
83                 "PATH", path, "INTERFACE", interface);
84             throw std::runtime_error("Error no matching service");
85         }
86     }
87     catch (const sdbusplus::exception_t& e)
88     {
89         error("Error in mapper call with path {PATH}, interface "
90               "{INTERFACE}, and exception {ERROR}",
91               "PATH", path, "INTERFACE", interface, "ERROR", e);
92         throw;
93     }
94 
95     return mapperResponse.begin()->first;
96 }
97 
getProperty(sdbusplus::bus_t & bus,const std::string & path,const std::string & interface,const std::string & propertyName)98 std::string getProperty(sdbusplus::bus_t& bus, const std::string& path,
99                         const std::string& interface,
100                         const std::string& propertyName)
101 {
102     std::variant<std::string> property;
103     std::string service = getService(bus, path, interface);
104 
105     auto method = bus.new_method_call(service.c_str(), path.c_str(),
106                                       PROPERTY_INTERFACE, "Get");
107 
108     method.append(interface, propertyName);
109 
110     try
111     {
112         auto reply = bus.call(method);
113         reply.read(property);
114     }
115     catch (const sdbusplus::exception_t& e)
116     {
117         error("Error in property Get, error {ERROR}, property {PROPERTY}",
118               "ERROR", e, "PROPERTY", propertyName);
119         throw;
120     }
121 
122     if (std::get<std::string>(property).empty())
123     {
124         error("Error reading property response for {PROPERTY}", "PROPERTY",
125               propertyName);
126         throw std::runtime_error("Error reading property response");
127     }
128 
129     return std::get<std::string>(property);
130 }
131 
setProperty(sdbusplus::bus_t & bus,const std::string & path,const std::string & interface,const std::string & property,const std::string & value)132 void setProperty(sdbusplus::bus_t& bus, const std::string& path,
133                  const std::string& interface, const std::string& property,
134                  const std::string& value)
135 {
136     std::variant<std::string> variantValue = value;
137     std::string service = getService(bus, path, interface);
138 
139     auto method = bus.new_method_call(service.c_str(), path.c_str(),
140                                       PROPERTY_INTERFACE, "Set");
141 
142     method.append(interface, property, variantValue);
143     bus.call_noreply(method);
144 
145     return;
146 }
147 
getGpioValue(const std::string & gpioName)148 int getGpioValue(const std::string& gpioName)
149 {
150     int gpioval = -1;
151     gpiod_line* line = gpiod_line_find(gpioName.c_str());
152 
153     if (nullptr != line)
154     {
155         // take ownership of gpio
156         if (0 != gpiod_line_request_input(line, "state-manager"))
157         {
158             error("Failed request for {GPIO_NAME} GPIO", "GPIO_NAME", gpioName);
159         }
160         else
161         {
162             // get gpio value
163             gpioval = gpiod_line_get_value(line);
164 
165             // release ownership of gpio
166             gpiod_line_close_chip(line);
167         }
168     }
169     return gpioval;
170 }
171 
createError(sdbusplus::bus_t & bus,const std::string & errorMsg,sdbusplus::server::xyz::openbmc_project::logging::Entry::Level errLevel,std::map<std::string,std::string> additionalData)172 void createError(
173     sdbusplus::bus_t& bus, const std::string& errorMsg,
174     sdbusplus::server::xyz::openbmc_project::logging::Entry::Level errLevel,
175     std::map<std::string, std::string> additionalData)
176 {
177     try
178     {
179         // Always add the _PID on for some extra logging debug
180         additionalData.emplace("_PID", std::to_string(getpid()));
181 
182         using LoggingCreate =
183             sdbusplus::client::xyz::openbmc_project::logging::Create<>;
184 
185         auto method = bus.new_method_call(LoggingCreate::default_service,
186                                           LoggingCreate::instance_path,
187                                           LoggingCreate::interface, "Create");
188 
189         method.append(errorMsg, errLevel, additionalData);
190         auto resp = bus.call(method);
191     }
192     catch (const sdbusplus::exception_t& e)
193     {
194         error("sdbusplus D-Bus call exception, error {ERROR} trying to create "
195               "an error with {ERROR_MSG}",
196               "ERROR", e, "ERROR_MSG", errorMsg);
197 
198         throw std::runtime_error(
199             "Error in invoking D-Bus logging create interface");
200     }
201     catch (const std::exception& e)
202     {
203         error("D-bus call exception: {ERROR}", "ERROR", e);
204         throw e;
205     }
206 }
207 
createBmcDump(sdbusplus::bus_t & bus)208 void createBmcDump(sdbusplus::bus_t& bus)
209 {
210     using DumpCreate = sdbusplus::client::xyz::openbmc_project::dump::Create<>;
211     auto dumpPath =
212         sdbusplus::message::object_path(DumpCreate::namespace_path::value) /
213         DumpCreate::namespace_path::bmc;
214 
215     auto method =
216         bus.new_method_call(DumpCreate::default_service, dumpPath.str.c_str(),
217                             DumpCreate::interface, "CreateDump");
218     method.append(
219         std::vector<
220             std::pair<std::string, std::variant<std::string, uint64_t>>>());
221     try
222     {
223         bus.call_noreply(method);
224     }
225     catch (const sdbusplus::exception_t& e)
226     {
227         error("Failed to create BMC dump, exception:{ERROR}", "ERROR", e);
228         // just continue, this is error path anyway so we're just collecting
229         // what we can
230     }
231 }
232 
checkACLoss(size_t & chassisId)233 bool checkACLoss(size_t& chassisId)
234 {
235     std::string chassisLostPowerFileFmt =
236         std::format(CHASSIS_LOST_POWER_FILE, chassisId);
237 
238     std::filesystem::path chassisPowerLossFile{chassisLostPowerFileFmt};
239     return std::filesystem::exists(chassisPowerLossFile);
240 }
241 
isBmcReady(sdbusplus::bus_t & bus)242 bool isBmcReady(sdbusplus::bus_t& bus)
243 {
244     using BMC = sdbusplus::client::xyz::openbmc_project::state::BMC<>;
245     auto bmcPath = sdbusplus::message::object_path(BMC::namespace_path::value) /
246                    BMC::namespace_path::bmc;
247 
248     auto bmcState =
249         getProperty(bus, bmcPath.str, BMC::interface, "CurrentBMCState");
250 
251     if (sdbusplus::message::convert_from_string<BMC::BMCState>(bmcState) !=
252         BMC::BMCState::Ready)
253     {
254         debug("BMC State is {BMC_STATE}", "BMC_STATE", bmcState);
255         return false;
256     }
257     return true;
258 }
259 
waitBmcReady(sdbusplus::bus_t & bus,std::chrono::seconds timeout)260 bool waitBmcReady(sdbusplus::bus_t& bus, std::chrono::seconds timeout)
261 {
262     while (timeout.count() != 0)
263     {
264         timeout--;
265         if (isBmcReady(bus))
266         {
267             return true;
268         }
269         std::this_thread::sleep_for(std::chrono::seconds(1));
270     }
271     return false;
272 }
273 
274 #ifdef CHECK_FWUPDATE_BEFORE_DO_TRANSITION
isFirmwareUpdating(sdbusplus::bus_t & bus)275 bool isFirmwareUpdating(sdbusplus::bus_t& bus)
276 {
277     /*
278      * This method looks for ActivationBlocksTransition interface, if any object
279      * path is including this interface, the Transition action should be
280      * prevented.
281      */
282     auto mapper = bus.new_method_call(
283         ObjectMapper::default_service, ObjectMapper::instance_path,
284         ObjectMapper::interface, "GetSubTreePaths");
285 
286     mapper.append("/", 0, std::vector<std::string>({ActBlockTrans::interface}));
287 
288     std::vector<std::string> mapperResponse;
289 
290     try
291     {
292         auto mapperResponseMsg = bus.call(mapper);
293 
294         mapperResponseMsg.read(mapperResponse);
295     }
296     catch (const sdbusplus::exception_t& e)
297     {
298         error("Error in mapper call with root path, interface "
299               "ActivationBlocksTransition, and exception {ERROR}",
300               "ERROR", e);
301         return false;
302     }
303 
304     return !mapperResponse.empty();
305 }
306 #endif // CHECK_FWUPDATE_BEFORE_DO_TRANSITION
307 
308 } // namespace utils
309 } // namespace manager
310 } // namespace state
311 } // namespace phosphor
312