xref: /openbmc/openpower-hw-diags/util/dbus.cpp (revision 626270af)
1 #include <fmt/format.h>
2 
3 #include <util/dbus.hpp>
4 #include <util/trace.hpp>
5 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
6 
7 namespace util
8 {
9 
10 namespace dbus
11 {
12 
13 //------------------------------------------------------------------------------
14 
15 constexpr auto objectMapperService   = "xyz.openbmc_project.ObjectMapper";
16 constexpr auto objectMapperPath      = "/xyz/openbmc_project/object_mapper";
17 constexpr auto objectMapperInterface = "xyz.openbmc_project.ObjectMapper";
18 
19 /** @brief Find the path and service that implements the given interface */
20 int find(const std::string& i_interface, std::string& o_path,
21          std::string& o_service)
22 {
23     int rc = 1; // assume not success
24 
25     auto bus = sdbusplus::bus::new_default();
26 
27     try
28     {
29         constexpr auto function = "GetSubTree";
30 
31         auto method = bus.new_method_call(objectMapperService, objectMapperPath,
32                                           objectMapperInterface, function);
33 
34         // Search the entire dbus tree for the specified interface
35         method.append(std::string{"/"}, 0,
36                       std::vector<std::string>{i_interface});
37 
38         auto reply = bus.call(method);
39 
40         DBusSubTree response;
41         reply.read(response);
42 
43         if (!response.empty())
44         {
45             // Response is a map of object paths to a map of service, interfaces
46             auto object = *(response.begin());
47             o_path      = object.first;                 // return path
48             o_service   = object.second.begin()->first; // return service
49 
50             rc = 0; // success
51         }
52     }
53     catch (const sdbusplus::exception::SdBusError& e)
54     {
55         trace::err("util::dbus::find exception");
56         std::string traceMsg = std::string(e.what());
57         trace::err(traceMsg.c_str());
58     }
59 
60     return rc;
61 }
62 
63 /** @brief Find the service that implements the given object and interface */
64 int findService(const std::string& i_interface, const std::string& i_path,
65                 std::string& o_service)
66 {
67     int rc = 1; // assume not success
68 
69     auto bus = sdbusplus::bus::new_default();
70 
71     try
72     {
73         constexpr auto function = "GetObject";
74 
75         auto method = bus.new_method_call(objectMapperService, objectMapperPath,
76                                           objectMapperInterface, function);
77 
78         // Find services that implement the object path, constrain the search
79         // to the given interface.
80         method.append(i_path, std::vector<std::string>{i_interface});
81 
82         auto reply = bus.call(method);
83 
84         // response is a map of service names to their interfaces
85         std::map<DBusService, DBusInterfaceList> response;
86         reply.read(response);
87 
88         if (!response.empty())
89         {
90             // return the service
91             o_service = response.begin()->first;
92 
93             rc = 0; // success
94         }
95     }
96     catch (const sdbusplus::exception::SdBusError& e)
97     {
98         trace::err("util::dbus::map exception");
99         std::string traceMsg = std::string(e.what());
100         trace::err(traceMsg.c_str());
101     }
102 
103     return rc;
104 }
105 
106 /** @brief Read a property from a dbus object interface */
107 int getProperty(const std::string& i_interface, const std::string& i_path,
108                 const std::string& i_service, const std::string& i_property,
109                 DBusValue& o_response)
110 {
111     int rc = 1; // assume not success
112 
113     auto bus = sdbusplus::bus::new_default();
114 
115     try
116     {
117         constexpr auto interface = "org.freedesktop.DBus.Properties";
118         constexpr auto function  = "Get";
119 
120         // calling the get property method
121         auto method = bus.new_method_call(i_service.c_str(), i_path.c_str(),
122                                           interface, function);
123 
124         method.append(i_interface, i_property);
125         auto reply = bus.call(method);
126 
127         // returning the property value
128         reply.read(o_response);
129 
130         rc = 0; // success
131     }
132     catch (const sdbusplus::exception::SdBusError& e)
133     {
134         trace::err("util::dbus::getProperty exception");
135         std::string traceMsg = std::string(e.what());
136         trace::err(traceMsg.c_str());
137     }
138 
139     return rc;
140 }
141 
142 /** @brief Get the IBM compatible names defined for this system */
143 std::vector<std::string> systemNames()
144 {
145     std::vector<std::string> names;
146 
147     constexpr auto interface =
148         "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
149 
150     DBusService service;
151     DBusPath path;
152 
153     // find a dbus object and path that implements the interface
154     if (0 == find(interface, path, service))
155     {
156         DBusValue value;
157 
158         // compatible system names are implemented as a property
159         constexpr auto property = "Names";
160 
161         if (0 == getProperty(interface, path, service, property, value))
162         {
163             // return value is a variant, names are in the vector
164             names = std::get<std::vector<std::string>>(value);
165         }
166     }
167 
168     return names;
169 }
170 
171 /** @brief Transition the host state */
172 void transitionHost(const HostState i_hostState)
173 {
174     try
175     {
176         // We will be transitioning host by starting appropriate dbus target
177         std::string target = "obmc-host-quiesce@0.target"; // quiesce is default
178 
179         // crash (mpipl) mode state requested
180         if (HostState::Crash == i_hostState)
181         {
182             target = "obmc-host-crash@0.target";
183         }
184 
185         auto bus    = sdbusplus::bus::new_system();
186         auto method = bus.new_method_call(
187             "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
188             "org.freedesktop.systemd1.Manager", "StartUnit");
189 
190         method.append(target);    // target unit to start
191         method.append("replace"); // mode = replace conflicting queued jobs
192 
193         bus.call_noreply(method); // start the service
194     }
195     catch (const sdbusplus::exception::SdBusError& e)
196     {
197         trace::err("util::dbus::transitionHost exception");
198         std::string traceMsg = std::string(e.what());
199         trace::err(traceMsg.c_str());
200     }
201 }
202 
203 /** @brief Read state of autoRebootEnabled property via dbus */
204 bool autoRebootEnabled()
205 {
206     // Assume true in case autoRebootEnbabled property is not available
207     bool autoReboot = true;
208 
209     constexpr auto interface = "xyz.openbmc_project.Control.Boot.RebootPolicy";
210 
211     DBusService service; // will find this
212     DBusPath path;       // will find this
213 
214     // find a dbus object and path that implements the interface
215     if (0 == find(interface, path, service))
216     {
217         DBusValue value;
218 
219         // autoreboot policy is implemented as a property
220         constexpr auto property = "AutoReboot";
221 
222         if (0 == getProperty(interface, path, service, property, value))
223         {
224             // return value is a variant, autoreboot policy is boolean
225             autoReboot = std::get<bool>(value);
226         }
227     }
228 
229     return autoReboot;
230 }
231 
232 /** @brief Get the running state of the host */
233 HostRunningState hostRunningState()
234 {
235     // assume not able to get host running state
236     HostRunningState host = HostRunningState::Unknown;
237 
238     constexpr auto interface = "xyz.openbmc_project.State.Boot.Progress";
239 
240     DBusService service;
241     DBusPath path;
242 
243     // find a dbus object and path that implements the interface
244     if (0 == find(interface, path, service))
245     {
246         DBusValue value;
247 
248         // boot progress is implemented as a property
249         constexpr auto property = "BootProgress";
250 
251         if (0 == getProperty(interface, path, service, property, value))
252         {
253             // return value is a variant, progress is in the vector of strings
254             std::string bootProgress(std::get<std::string>(value));
255 
256             // convert boot progress to host state
257             using BootProgress = sdbusplus::xyz::openbmc_project::State::Boot::
258                 server::Progress::ProgressStages;
259 
260             BootProgress stage = sdbusplus::xyz::openbmc_project::State::Boot::
261                 server::Progress::convertProgressStagesFromString(bootProgress);
262 
263             if ((stage == BootProgress::SystemInitComplete) ||
264                 (stage == BootProgress::OSStart) ||
265                 (stage == BootProgress::OSRunning))
266             {
267                 host = HostRunningState::Started;
268             }
269             else
270             {
271                 host = HostRunningState::NotStarted;
272             }
273         }
274     }
275 
276     return host;
277 }
278 
279 /** @brief Read state of dumpPolicyEnabled property via dbus */
280 bool dumpPolicyEnabled()
281 {
282     // Assume true In case dumpPolicyEnabled property is not available
283     bool dumpPolicyEnabled = true;
284 
285     constexpr auto interface = "xyz.openbmc_project.Object.Enable";
286     constexpr auto path      = "/xyz/openbmc_project/dump/system_dump_policy";
287 
288     DBusService service; // will find this
289 
290     // find a dbus object and path that implements the interface
291     if (0 == findService(interface, path, service))
292     {
293         DBusValue value;
294 
295         // autoreboot policy is implemented as a property
296         constexpr auto property = "Enabled";
297 
298         if (0 == getProperty(interface, path, service, property, value))
299         {
300             // return value is a variant, dump policy enabled is a boolean
301             dumpPolicyEnabled = std::get<bool>(value);
302         }
303     }
304 
305     return dumpPolicyEnabled;
306 }
307 
308 /** @brief Create a PEL */
309 uint32_t createPel(const std::string& i_message, const std::string& i_severity,
310                    std::map<std::string, std::string>& io_additional,
311                    const std::vector<util::FFDCTuple>& i_ffdc)
312 {
313     // CreatePELWithFFDCFiles returns plid
314     int plid = 0;
315 
316     // Sdbus call specifics
317     constexpr auto interface = "org.open_power.Logging.PEL";
318     constexpr auto path      = "/xyz/openbmc_project/logging";
319 
320     // we need to find the service implementing the interface
321     util::dbus::DBusService service;
322 
323     if (0 == findService(interface, path, service))
324     {
325         try
326         {
327             constexpr auto function = "CreatePELWithFFDCFiles";
328 
329             // The "Create" method requires manually adding the process ID.
330             io_additional["_PID"] = std::to_string(getpid());
331 
332             // create dbus method
333             auto bus = sdbusplus::bus::new_system();
334             sdbusplus::message::message method =
335                 bus.new_method_call(service.c_str(), path, interface, function);
336 
337             // append additional dbus call paramaters
338             method.append(i_message, i_severity, io_additional, i_ffdc);
339 
340             // using system dbus
341             auto response = bus.call(method);
342 
343             // reply will be tuple containing bmc log id, platform log id
344             std::tuple<uint32_t, uint32_t> reply = {0, 0};
345 
346             // parse dbus response into reply
347             response.read(reply);
348             plid = std::get<1>(reply); // platform log id is tuple "second"
349         }
350         catch (const sdbusplus::exception::SdBusError& e)
351         {
352             trace::err("createPel exception");
353             trace::err(e.what());
354         }
355     }
356 
357     return plid; // platform log id or 0
358 }
359 
360 MachineType getMachineType()
361 {
362     // default to Rainier 2S4U
363     MachineType machineType = MachineType::Rainier_2S4U;
364 
365     // The return value of the dbus operation is a vector of 4 uint8_ts
366     std::vector<uint8_t> ids;
367 
368     constexpr auto interface = "com.ibm.ipzvpd.VSBP";
369 
370     DBusService service;
371     DBusPath path;
372 
373     if (0 == find(interface, path, service))
374     {
375         DBusValue value;
376 
377         // Machine ID is given from the "IM" keyword
378         constexpr auto property = "IM";
379 
380         if (0 == getProperty(interface, path, service, property, value))
381         {
382             // return value is a variant, ID value is a vector of 4 uint8_ts
383             ids = std::get<std::vector<uint8_t>>(value);
384 
385             // Convert the returned ID value to a hex string to determine
386             // machine type. The hex values corresponding to the machine type
387             // are defined in /openbmc/openpower-vpd-parser/const.hpp
388             // RAINIER_2S4U == 0x50001000
389             // RAINIER_2S2U == 0x50001001
390             // RAINIER_1S4U == 0x50001002
391             // RAINIER_1S2U == 0x50001003
392             // EVEREST      == 0x50003000
393             try
394             {
395                 // Format the vector into a single hex string to compare to.
396                 std::string hexId =
397                     fmt::format("0x{:02x}{:02x}{:02x}{:02x}", ids.at(0),
398                                 ids.at(1), ids.at(2), ids.at(3));
399 
400                 std::map<std::string, MachineType> typeMap = {
401                     {"0x50001000", MachineType::Rainier_2S4U},
402                     {"0x50001001", MachineType::Rainier_2S2U},
403                     {"0x50001002", MachineType::Rainier_1S4U},
404                     {"0x50001003", MachineType::Rainier_1S2U},
405                     {"0x50003000", MachineType::Everest},
406                 };
407 
408                 machineType = typeMap.at(hexId);
409             }
410             catch (const std::out_of_range& e)
411             {
412                 trace::err("Out of range exception caught from returned "
413                            "machine ID.");
414                 for (const auto& id : ids)
415                 {
416                     trace::err("Returned Machine ID value: 0x%x", id);
417                 }
418                 throw;
419             }
420         }
421     }
422     else
423     {
424         throw std::invalid_argument(
425             "Unable to find dbus service to get machine type.");
426     }
427 
428     return machineType;
429 }
430 
431 } // namespace dbus
432 
433 } // namespace util
434