xref: /openbmc/openpower-hw-diags/util/dbus.cpp (revision 5d2e99124024c3e63180897d96a6688ef23d8d89)
1 #include <util/dbus.hpp>
2 #include <util/trace.hpp>
3 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
4 
5 #include <format>
6 
7 namespace util
8 {
9 namespace dbus
10 {
11 //------------------------------------------------------------------------------
12 
13 constexpr auto objectMapperService = "xyz.openbmc_project.ObjectMapper";
14 constexpr auto objectMapperPath = "/xyz/openbmc_project/object_mapper";
15 constexpr auto objectMapperInterface = "xyz.openbmc_project.ObjectMapper";
16 
17 constexpr uint8_t terminusIdZero = 0;
18 
19 /** @brief Find the path and service that implements the given interface */
find(const std::string & i_interface,std::string & o_path,std::string & o_service)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         auto response = reply.unpack<DBusSubTree>();
41 
42         if (!response.empty())
43         {
44             // Response is a map of object paths to a map of service, interfaces
45             auto object = *(response.begin());
46             o_path = object.first;                    // return path
47             o_service = object.second.begin()->first; // return service
48 
49             rc = 0;                                   // success
50         }
51     }
52     catch (const sdbusplus::exception::SdBusError& e)
53     {
54         trace::err("util::dbus::find exception");
55         std::string traceMsg = std::string(e.what());
56         trace::err(traceMsg.c_str());
57     }
58 
59     return rc;
60 }
61 
62 /** @brief Find the service that implements the given object and interface */
findService(const std::string & i_interface,const std::string & i_path,std::string & o_service)63 int findService(const std::string& i_interface, const std::string& i_path,
64                 std::string& o_service)
65 {
66     int rc = 1; // assume not success
67 
68     auto bus = sdbusplus::bus::new_default();
69 
70     try
71     {
72         constexpr auto function = "GetObject";
73 
74         auto method = bus.new_method_call(objectMapperService, objectMapperPath,
75                                           objectMapperInterface, function);
76 
77         // Find services that implement the object path, constrain the search
78         // to the given interface.
79         method.append(i_path, std::vector<std::string>{i_interface});
80 
81         auto reply = bus.call(method);
82 
83         // response is a map of service names to their interfaces
84         auto response =
85             reply.unpack<std::map<DBusService, DBusInterfaceList>>();
86 
87         if (!response.empty())
88         {
89             // return the service
90             o_service = response.begin()->first;
91 
92             rc = 0; // success
93         }
94     }
95     catch (const sdbusplus::exception::SdBusError& e)
96     {
97         trace::err("util::dbus::map exception");
98         std::string traceMsg = std::string(e.what());
99         trace::err(traceMsg.c_str());
100     }
101 
102     return rc;
103 }
104 
105 /** @brief Read a property from a dbus object interface */
getProperty(const std::string & i_interface,const std::string & i_path,const std::string & i_service,const std::string & i_property,DBusValue & o_response)106 int getProperty(const std::string& i_interface, const std::string& i_path,
107                 const std::string& i_service, const std::string& i_property,
108                 DBusValue& o_response)
109 {
110     int rc = 1; // assume not success
111 
112     auto bus = sdbusplus::bus::new_default();
113 
114     try
115     {
116         constexpr auto interface = "org.freedesktop.DBus.Properties";
117         constexpr auto function = "Get";
118 
119         // calling the get property method
120         auto method = bus.new_method_call(i_service.c_str(), i_path.c_str(),
121                                           interface, function);
122 
123         method.append(i_interface, i_property);
124         auto reply = bus.call(method);
125 
126         // returning the property value
127         reply.read(o_response);
128 
129         rc = 0; // success
130     }
131     catch (const sdbusplus::exception::SdBusError& e)
132     {
133         trace::err("util::dbus::getProperty exception");
134         std::string traceMsg = std::string(e.what());
135         trace::err(traceMsg.c_str());
136     }
137 
138     return rc;
139 }
140 
141 /** @brief Get the IBM compatible names defined for this system */
systemNames()142 std::vector<std::string> systemNames()
143 {
144     std::vector<std::string> names;
145 
146     constexpr auto interface =
147         "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
148 
149     DBusService service;
150     DBusPath path;
151 
152     // find a dbus object and path that implements the interface
153     if (0 == find(interface, path, service))
154     {
155         DBusValue value;
156 
157         // compatible system names are implemented as a property
158         constexpr auto property = "Names";
159 
160         if (0 == getProperty(interface, path, service, property, value))
161         {
162             // return value is a variant, names are in the vector
163             names = std::get<std::vector<std::string>>(value);
164         }
165     }
166 
167     return names;
168 }
169 
170 /** @brief Transition the host state */
transitionHost(const HostState i_hostState)171 void transitionHost(const HostState i_hostState)
172 {
173     try
174     {
175         // We will be transitioning host by starting appropriate dbus target
176         std::string target = "obmc-host-quiesce@0.target"; // quiesce is default
177 
178         // crash (mpipl) mode state requested
179         if (HostState::Crash == i_hostState)
180         {
181             target = "obmc-host-crash@0.target";
182         }
183 
184         // If the system is powering off for any reason (ex. we hit a PHYP TI
185         // in the graceful power off path), then we want to call the immediate
186         // power off target
187         if (hostRunningState() == HostRunningState::Stopping)
188         {
189             trace::inf("system is powering off so no dump will be requested");
190             target = "obmc-chassis-hard-poweroff@0.target";
191         }
192 
193         auto bus = sdbusplus::bus::new_system();
194         auto method = bus.new_method_call(
195             "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
196             "org.freedesktop.systemd1.Manager", "StartUnit");
197 
198         method.append(target);    // target unit to start
199         method.append("replace"); // mode = replace conflicting queued jobs
200 
201         bus.call_noreply(method); // start the service
202     }
203     catch (const sdbusplus::exception::SdBusError& e)
204     {
205         trace::err("util::dbus::transitionHost exception");
206         std::string traceMsg = std::string(e.what());
207         trace::err(traceMsg.c_str());
208     }
209 }
210 
211 /** @brief Read state of autoRebootEnabled property via dbus */
autoRebootEnabled()212 bool autoRebootEnabled()
213 {
214     // Assume true in case autoRebootEnbabled property is not available
215     bool autoReboot = true;
216 
217     constexpr auto interface = "xyz.openbmc_project.Control.Boot.RebootPolicy";
218 
219     DBusService service; // will find this
220     DBusPath path;       // will find this
221 
222     // find a dbus object and path that implements the interface
223     if (0 == find(interface, path, service))
224     {
225         DBusValue value;
226 
227         // autoreboot policy is implemented as a property
228         constexpr auto property = "AutoReboot";
229 
230         if (0 == getProperty(interface, path, service, property, value))
231         {
232             // return value is a variant, autoreboot policy is boolean
233             autoReboot = std::get<bool>(value);
234         }
235     }
236 
237     return autoReboot;
238 }
239 
240 /** @brief Get the running state of the host */
hostRunningState()241 HostRunningState hostRunningState()
242 {
243     // assume not able to get host running state
244     HostRunningState host = HostRunningState::Unknown;
245 
246     constexpr auto interface = "xyz.openbmc_project.State.Boot.Progress";
247 
248     DBusService service;
249     DBusPath path;
250 
251     // find a dbus object and path that implements the interface
252     if (0 == find(interface, path, service))
253     {
254         DBusValue value;
255 
256         // boot progress is implemented as a property
257         constexpr auto property = "BootProgress";
258 
259         if (0 == getProperty(interface, path, service, property, value))
260         {
261             // return value is a variant, progress is in the vector of strings
262             std::string bootProgress(std::get<std::string>(value));
263 
264             // convert boot progress to host state
265             using BootProgress = sdbusplus::xyz::openbmc_project::State::Boot::
266                 server::Progress::ProgressStages;
267 
268             BootProgress stage = sdbusplus::xyz::openbmc_project::State::Boot::
269                 server::Progress::convertProgressStagesFromString(bootProgress);
270 
271             if ((stage == BootProgress::SystemInitComplete) ||
272                 (stage == BootProgress::OSRunning))
273             {
274                 host = HostRunningState::Started;
275             }
276             else
277             {
278                 host = HostRunningState::NotStarted;
279             }
280         }
281     }
282 
283     // See if host in process of powering off when we get NotStarted
284     if (host == HostRunningState::NotStarted)
285     {
286         constexpr auto hostStateInterface = "xyz.openbmc_project.State.Host";
287         if (0 == find(hostStateInterface, path, service))
288         {
289             DBusValue value;
290 
291             // current host state is implemented as a property
292             constexpr auto stateProperty = "CurrentHostState";
293 
294             if (0 == getProperty(hostStateInterface, path, service,
295                                  stateProperty, value))
296             {
297                 // return value is a variant, host state is in the vector of
298                 // strings
299                 std::string hostState(std::get<std::string>(value));
300                 if (hostState == "xyz.openbmc_project.State.Host.HostState."
301                                  "TransitioningToOff")
302                 {
303                     host = HostRunningState::Stopping;
304                 }
305             }
306         }
307     }
308 
309     return host;
310 }
311 
312 /** @brief Read state of dumpPolicyEnabled property via dbus */
dumpPolicyEnabled()313 bool dumpPolicyEnabled()
314 {
315     // Assume true In case dumpPolicyEnabled property is not available
316     bool dumpPolicyEnabled = true;
317 
318     constexpr auto interface = "xyz.openbmc_project.Object.Enable";
319     constexpr auto path = "/xyz/openbmc_project/dump/system_dump_policy";
320 
321     DBusService service; // will find this
322 
323     // find a dbus object and path that implements the interface
324     if (0 == findService(interface, path, service))
325     {
326         DBusValue value;
327 
328         // autoreboot policy is implemented as a property
329         constexpr auto property = "Enabled";
330 
331         if (0 == getProperty(interface, path, service, property, value))
332         {
333             // return value is a variant, dump policy enabled is a boolean
334             dumpPolicyEnabled = std::get<bool>(value);
335         }
336     }
337 
338     return dumpPolicyEnabled;
339 }
340 
341 /** @brief Create a PEL */
createPel(const std::string & i_message,const std::string & i_severity,std::map<std::string,std::string> & io_additional,const std::vector<util::FFDCTuple> & i_ffdc)342 uint32_t createPel(const std::string& i_message, const std::string& i_severity,
343                    std::map<std::string, std::string>& io_additional,
344                    const std::vector<util::FFDCTuple>& i_ffdc)
345 {
346     // CreatePELWithFFDCFiles returns plid
347     int plid = 0;
348 
349     // Sdbus call specifics
350     constexpr auto interface = "org.open_power.Logging.PEL";
351     constexpr auto path = "/xyz/openbmc_project/logging";
352 
353     // we need to find the service implementing the interface
354     util::dbus::DBusService service;
355 
356     if (0 == findService(interface, path, service))
357     {
358         try
359         {
360             constexpr auto function = "CreatePELWithFFDCFiles";
361 
362             // The "Create" method requires manually adding the process ID.
363             io_additional["_PID"] = std::to_string(getpid());
364 
365             // create dbus method
366             auto bus = sdbusplus::bus::new_system();
367             sdbusplus::message_t method =
368                 bus.new_method_call(service.c_str(), path, interface, function);
369 
370             // append additional dbus call paramaters
371             method.append(i_message, i_severity, io_additional, i_ffdc);
372 
373             // using system dbus
374             auto response = bus.call(method);
375 
376             // reply will be tuple containing bmc log id, platform log id
377             std::tuple<uint32_t, uint32_t> reply = {0, 0};
378 
379             // parse dbus response into reply
380             response.read(reply);
381             plid = std::get<1>(reply); // platform log id is tuple "second"
382         }
383         catch (const sdbusplus::exception::SdBusError& e)
384         {
385             trace::err("createPel exception");
386             trace::err(e.what());
387         }
388     }
389 
390     return plid; // platform log id or 0
391 }
392 
getMachineType()393 MachineType getMachineType()
394 {
395     // default to Rainier 2S4U
396     MachineType machineType = MachineType::Rainier_2S4U;
397 
398     // The return value of the dbus operation is a vector of 4 uint8_ts
399     std::vector<uint8_t> ids;
400 
401     constexpr auto interface = "com.ibm.ipzvpd.VSBP";
402 
403     DBusService service;
404     DBusPath path;
405 
406     if (0 == find(interface, path, service))
407     {
408         DBusValue value;
409 
410         // Machine ID is given from the "IM" keyword
411         constexpr auto property = "IM";
412 
413         if (0 == getProperty(interface, path, service, property, value))
414         {
415             // return value is a variant, ID value is a vector of 4 uint8_ts
416             ids = std::get<std::vector<uint8_t>>(value);
417 
418             // Convert the returned ID value to a hex string to determine
419             // machine type. The hex values corresponding to the machine type
420             // are defined in /openbmc/openpower-vpd-parser/const.hpp
421             // RAINIER_2S4U == 0x50001000
422             // RAINIER_2S2U == 0x50001001
423             // RAINIER_1S4U == 0x50001002
424             // RAINIER_1S2U == 0x50001003
425             // EVEREST      == 0x50003000
426             // BONNELL      == 0x50004000
427             try
428             {
429                 // Format the vector into a single hex string to compare to.
430                 std::string hexId =
431                     std::format("0x{:02x}{:02x}{:02x}{:02x}", ids.at(0),
432                                 ids.at(1), ids.at(2), ids.at(3));
433 
434                 std::map<std::string, MachineType> typeMap = {
435                     {"0x50001000", MachineType::Rainier_2S4U},
436                     {"0x50001001", MachineType::Rainier_2S2U},
437                     {"0x50001002", MachineType::Rainier_1S4U},
438                     {"0x50001003", MachineType::Rainier_1S2U},
439                     {"0x50003000", MachineType::Everest},
440                     {"0x50004000", MachineType::Bonnell},
441                     {"0x60001000", MachineType::BlueRidge_2S4U},
442                     {"0x60001001", MachineType::BlueRidge_2S2U},
443                     {"0x60001002", MachineType::BlueRidge_1S4U},
444                     {"0x60002000", MachineType::Fuji},
445                     {"0x60004000", MachineType::Balcones},
446                 };
447 
448                 machineType = typeMap.at(hexId);
449             }
450             catch (const std::out_of_range& e)
451             {
452                 trace::err("Out of range exception caught from returned "
453                            "machine ID.");
454                 for (const auto& id : ids)
455                 {
456                     trace::err("Returned Machine ID value: 0x%x", id);
457                 }
458                 throw;
459             }
460         }
461     }
462     else
463     {
464         throw std::invalid_argument(
465             "Unable to find dbus service to get machine type.");
466     }
467 
468     return machineType;
469 }
470 
471 /** @brief Get list of state effecter PDRs */
getStateEffecterPdrs(std::vector<std::vector<uint8_t>> & pdrList,uint16_t stateSetId)472 bool getStateEffecterPdrs(std::vector<std::vector<uint8_t>>& pdrList,
473                           uint16_t stateSetId)
474 {
475     constexpr auto service = "xyz.openbmc_project.PLDM";
476     constexpr auto path = "/xyz/openbmc_project/pldm";
477     constexpr auto interface = "xyz.openbmc_project.PLDM.PDR";
478     constexpr auto function = "FindStateEffecterPDR";
479 
480     constexpr uint16_t PLDM_ENTITY_PROC = 135;
481 
482     try
483     {
484         // create dbus method
485         auto bus = sdbusplus::bus::new_default();
486         sdbusplus::message_t method =
487             bus.new_method_call(service, path, interface, function);
488 
489         // append additional method data
490         method.append(terminusIdZero, PLDM_ENTITY_PROC, stateSetId);
491 
492         // request PDRs
493         auto reply = bus.call(method);
494         reply.read(pdrList);
495     }
496     catch (const sdbusplus::exception_t& e)
497     {
498         trace::err("failed to find state effecter PDRs");
499         trace::err(e.what());
500         return false;
501     }
502 
503     return true;
504 }
505 
506 /** @brief Get list of state sensor PDRs */
getStateSensorPdrs(std::vector<std::vector<uint8_t>> & pdrList,uint16_t stateSetId)507 bool getStateSensorPdrs(std::vector<std::vector<uint8_t>>& pdrList,
508                         uint16_t stateSetId)
509 {
510     constexpr auto service = "xyz.openbmc_project.PLDM";
511     constexpr auto path = "/xyz/openbmc_project/pldm";
512     constexpr auto interface = "xyz.openbmc_project.PLDM.PDR";
513     constexpr auto function = "FindStateSensorPDR";
514 
515     constexpr uint16_t PLDM_ENTITY_PROC = 135;
516 
517     try
518     {
519         // create dbus method
520         auto bus = sdbusplus::bus::new_default();
521         sdbusplus::message_t method =
522             bus.new_method_call(service, path, interface, function);
523 
524         // append additional method data
525         method.append(terminusIdZero, PLDM_ENTITY_PROC, stateSetId);
526 
527         // request PDRs
528         auto reply = bus.call(method);
529         reply.read(pdrList);
530     }
531     catch (const sdbusplus::exception_t& e)
532     {
533         trace::err("failed to find state sensor PDRs");
534         trace::err(e.what());
535         return false;
536     }
537 
538     return true;
539 }
540 
541 /** @brief Determine if power fault was detected */
powerFault()542 bool powerFault()
543 {
544     // power fault based on pgood property
545     int32_t pgood = 0; // assume fault or unknown
546 
547     constexpr auto interface = "org.openbmc.control.Power";
548 
549     DBusService service;
550     DBusPath path;
551 
552     // find a dbus service and object path that implements the interface
553     if (0 == find(interface, path, service))
554     {
555         DBusValue value;
556 
557         // chassis pgood is implemented as a property
558         constexpr auto property = "pgood";
559 
560         if (0 == getProperty(interface, path, service, property, value))
561         {
562             // return value is a variant, int32 == 1 for pgood OK
563             pgood = std::get<int32_t>(value);
564         }
565     }
566 
567     return pgood != 1 ? true : false; // if not pgood then power fault
568 }
569 
570 } // namespace dbus
571 } // namespace util
572