xref: /openbmc/openpower-hw-diags/util/dbus.cpp (revision 08f25b21)
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         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 */
findService(const std::string & i_interface,const std::string & i_path,std::string & o_service)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 */
getProperty(const std::string & i_interface,const std::string & i_path,const std::string & i_service,const std::string & i_property,DBusValue & o_response)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 */
systemNames()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 */
transitionHost(const HostState i_hostState)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         // If the system is powering off for any reason (ex. we hit a PHYP TI
186         // in the graceful power off path), then we want to call the immediate
187         // power off target
188         if (hostRunningState() == HostRunningState::Stopping)
189         {
190             trace::inf("system is powering off so no dump will be requested");
191             target = "obmc-chassis-hard-poweroff@0.target";
192         }
193 
194         auto bus = sdbusplus::bus::new_system();
195         auto method = bus.new_method_call(
196             "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
197             "org.freedesktop.systemd1.Manager", "StartUnit");
198 
199         method.append(target);    // target unit to start
200         method.append("replace"); // mode = replace conflicting queued jobs
201 
202         bus.call_noreply(method); // start the service
203     }
204     catch (const sdbusplus::exception::SdBusError& e)
205     {
206         trace::err("util::dbus::transitionHost exception");
207         std::string traceMsg = std::string(e.what());
208         trace::err(traceMsg.c_str());
209     }
210 }
211 
212 /** @brief Read state of autoRebootEnabled property via dbus */
autoRebootEnabled()213 bool autoRebootEnabled()
214 {
215     // Assume true in case autoRebootEnbabled property is not available
216     bool autoReboot = true;
217 
218     constexpr auto interface = "xyz.openbmc_project.Control.Boot.RebootPolicy";
219 
220     DBusService service; // will find this
221     DBusPath path;       // will find this
222 
223     // find a dbus object and path that implements the interface
224     if (0 == find(interface, path, service))
225     {
226         DBusValue value;
227 
228         // autoreboot policy is implemented as a property
229         constexpr auto property = "AutoReboot";
230 
231         if (0 == getProperty(interface, path, service, property, value))
232         {
233             // return value is a variant, autoreboot policy is boolean
234             autoReboot = std::get<bool>(value);
235         }
236     }
237 
238     return autoReboot;
239 }
240 
241 /** @brief Get the running state of the host */
hostRunningState()242 HostRunningState hostRunningState()
243 {
244     // assume not able to get host running state
245     HostRunningState host = HostRunningState::Unknown;
246 
247     constexpr auto interface = "xyz.openbmc_project.State.Boot.Progress";
248 
249     DBusService service;
250     DBusPath path;
251 
252     // find a dbus object and path that implements the interface
253     if (0 == find(interface, path, service))
254     {
255         DBusValue value;
256 
257         // boot progress is implemented as a property
258         constexpr auto property = "BootProgress";
259 
260         if (0 == getProperty(interface, path, service, property, value))
261         {
262             // return value is a variant, progress is in the vector of strings
263             std::string bootProgress(std::get<std::string>(value));
264 
265             // convert boot progress to host state
266             using BootProgress = sdbusplus::xyz::openbmc_project::State::Boot::
267                 server::Progress::ProgressStages;
268 
269             BootProgress stage = sdbusplus::xyz::openbmc_project::State::Boot::
270                 server::Progress::convertProgressStagesFromString(bootProgress);
271 
272             if ((stage == BootProgress::SystemInitComplete) ||
273                 (stage == BootProgress::OSRunning))
274             {
275                 host = HostRunningState::Started;
276             }
277             else
278             {
279                 host = HostRunningState::NotStarted;
280             }
281         }
282     }
283 
284     // See if host in process of powering off when we get NotStarted
285     if (host == HostRunningState::NotStarted)
286     {
287         constexpr auto hostStateInterface = "xyz.openbmc_project.State.Host";
288         if (0 == find(hostStateInterface, path, service))
289         {
290             DBusValue value;
291 
292             // current host state is implemented as a property
293             constexpr auto stateProperty = "CurrentHostState";
294 
295             if (0 == getProperty(hostStateInterface, path, service,
296                                  stateProperty, value))
297             {
298                 // return value is a variant, host state is in the vector of
299                 // strings
300                 std::string hostState(std::get<std::string>(value));
301                 if (hostState == "xyz.openbmc_project.State.Host.HostState."
302                                  "TransitioningToOff")
303                 {
304                     host = HostRunningState::Stopping;
305                 }
306             }
307         }
308     }
309 
310     return host;
311 }
312 
313 /** @brief Read state of dumpPolicyEnabled property via dbus */
dumpPolicyEnabled()314 bool dumpPolicyEnabled()
315 {
316     // Assume true In case dumpPolicyEnabled property is not available
317     bool dumpPolicyEnabled = true;
318 
319     constexpr auto interface = "xyz.openbmc_project.Object.Enable";
320     constexpr auto path = "/xyz/openbmc_project/dump/system_dump_policy";
321 
322     DBusService service; // will find this
323 
324     // find a dbus object and path that implements the interface
325     if (0 == findService(interface, path, service))
326     {
327         DBusValue value;
328 
329         // autoreboot policy is implemented as a property
330         constexpr auto property = "Enabled";
331 
332         if (0 == getProperty(interface, path, service, property, value))
333         {
334             // return value is a variant, dump policy enabled is a boolean
335             dumpPolicyEnabled = std::get<bool>(value);
336         }
337     }
338 
339     return dumpPolicyEnabled;
340 }
341 
342 /** @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)343 uint32_t createPel(const std::string& i_message, const std::string& i_severity,
344                    std::map<std::string, std::string>& io_additional,
345                    const std::vector<util::FFDCTuple>& i_ffdc)
346 {
347     // CreatePELWithFFDCFiles returns plid
348     int plid = 0;
349 
350     // Sdbus call specifics
351     constexpr auto interface = "org.open_power.Logging.PEL";
352     constexpr auto path = "/xyz/openbmc_project/logging";
353 
354     // we need to find the service implementing the interface
355     util::dbus::DBusService service;
356 
357     if (0 == findService(interface, path, service))
358     {
359         try
360         {
361             constexpr auto function = "CreatePELWithFFDCFiles";
362 
363             // The "Create" method requires manually adding the process ID.
364             io_additional["_PID"] = std::to_string(getpid());
365 
366             // create dbus method
367             auto bus = sdbusplus::bus::new_system();
368             sdbusplus::message_t method =
369                 bus.new_method_call(service.c_str(), path, interface, function);
370 
371             // append additional dbus call paramaters
372             method.append(i_message, i_severity, io_additional, i_ffdc);
373 
374             // using system dbus
375             auto response = bus.call(method);
376 
377             // reply will be tuple containing bmc log id, platform log id
378             std::tuple<uint32_t, uint32_t> reply = {0, 0};
379 
380             // parse dbus response into reply
381             response.read(reply);
382             plid = std::get<1>(reply); // platform log id is tuple "second"
383         }
384         catch (const sdbusplus::exception::SdBusError& e)
385         {
386             trace::err("createPel exception");
387             trace::err(e.what());
388         }
389     }
390 
391     return plid; // platform log id or 0
392 }
393 
getMachineType()394 MachineType getMachineType()
395 {
396     // default to Rainier 2S4U
397     MachineType machineType = MachineType::Rainier_2S4U;
398 
399     // The return value of the dbus operation is a vector of 4 uint8_ts
400     std::vector<uint8_t> ids;
401 
402     constexpr auto interface = "com.ibm.ipzvpd.VSBP";
403 
404     DBusService service;
405     DBusPath path;
406 
407     if (0 == find(interface, path, service))
408     {
409         DBusValue value;
410 
411         // Machine ID is given from the "IM" keyword
412         constexpr auto property = "IM";
413 
414         if (0 == getProperty(interface, path, service, property, value))
415         {
416             // return value is a variant, ID value is a vector of 4 uint8_ts
417             ids = std::get<std::vector<uint8_t>>(value);
418 
419             // Convert the returned ID value to a hex string to determine
420             // machine type. The hex values corresponding to the machine type
421             // are defined in /openbmc/openpower-vpd-parser/const.hpp
422             // RAINIER_2S4U == 0x50001000
423             // RAINIER_2S2U == 0x50001001
424             // RAINIER_1S4U == 0x50001002
425             // RAINIER_1S2U == 0x50001003
426             // EVEREST      == 0x50003000
427             // BONNELL      == 0x50004000
428             try
429             {
430                 // Format the vector into a single hex string to compare to.
431                 std::string hexId =
432                     std::format("0x{:02x}{:02x}{:02x}{:02x}", ids.at(0),
433                                 ids.at(1), ids.at(2), ids.at(3));
434 
435                 std::map<std::string, MachineType> typeMap = {
436                     {"0x50001000", MachineType::Rainier_2S4U},
437                     {"0x50001001", MachineType::Rainier_2S2U},
438                     {"0x50001002", MachineType::Rainier_1S4U},
439                     {"0x50001003", MachineType::Rainier_1S2U},
440                     {"0x50003000", MachineType::Everest},
441                     {"0x50004000", MachineType::Bonnell},
442                 };
443 
444                 machineType = typeMap.at(hexId);
445             }
446             catch (const std::out_of_range& e)
447             {
448                 trace::err("Out of range exception caught from returned "
449                            "machine ID.");
450                 for (const auto& id : ids)
451                 {
452                     trace::err("Returned Machine ID value: 0x%x", id);
453                 }
454                 throw;
455             }
456         }
457     }
458     else
459     {
460         throw std::invalid_argument(
461             "Unable to find dbus service to get machine type.");
462     }
463 
464     return machineType;
465 }
466 
467 /** @brief Get list of state effecter PDRs */
getStateEffecterPdrs(std::vector<std::vector<uint8_t>> & pdrList,uint16_t stateSetId)468 bool getStateEffecterPdrs(std::vector<std::vector<uint8_t>>& pdrList,
469                           uint16_t stateSetId)
470 {
471     constexpr auto service = "xyz.openbmc_project.PLDM";
472     constexpr auto path = "/xyz/openbmc_project/pldm";
473     constexpr auto interface = "xyz.openbmc_project.PLDM.PDR";
474     constexpr auto function = "FindStateEffecterPDR";
475 
476     constexpr uint16_t PLDM_ENTITY_PROC = 135;
477 
478     try
479     {
480         // create dbus method
481         auto bus = sdbusplus::bus::new_default();
482         sdbusplus::message_t method =
483             bus.new_method_call(service, path, interface, function);
484 
485         // append additional method data
486         method.append(terminusIdZero, PLDM_ENTITY_PROC, stateSetId);
487 
488         // request PDRs
489         auto reply = bus.call(method);
490         reply.read(pdrList);
491     }
492     catch (const sdbusplus::exception_t& e)
493     {
494         trace::err("failed to find state effecter PDRs");
495         trace::err(e.what());
496         return false;
497     }
498 
499     return true;
500 }
501 
502 /** @brief Get list of state sensor PDRs */
getStateSensorPdrs(std::vector<std::vector<uint8_t>> & pdrList,uint16_t stateSetId)503 bool getStateSensorPdrs(std::vector<std::vector<uint8_t>>& pdrList,
504                         uint16_t stateSetId)
505 {
506     constexpr auto service = "xyz.openbmc_project.PLDM";
507     constexpr auto path = "/xyz/openbmc_project/pldm";
508     constexpr auto interface = "xyz.openbmc_project.PLDM.PDR";
509     constexpr auto function = "FindStateSensorPDR";
510 
511     constexpr uint16_t PLDM_ENTITY_PROC = 135;
512 
513     try
514     {
515         // create dbus method
516         auto bus = sdbusplus::bus::new_default();
517         sdbusplus::message_t method =
518             bus.new_method_call(service, path, interface, function);
519 
520         // append additional method data
521         method.append(terminusIdZero, PLDM_ENTITY_PROC, stateSetId);
522 
523         // request PDRs
524         auto reply = bus.call(method);
525         reply.read(pdrList);
526     }
527     catch (const sdbusplus::exception_t& e)
528     {
529         trace::err("failed to find state sensor PDRs");
530         trace::err(e.what());
531         return false;
532     }
533 
534     return true;
535 }
536 
537 /** @brief Get PLDM instance associated with endpoint */
getPldmInstanceID(uint8_t & pldmInstanceID,uint8_t Eid)538 bool getPldmInstanceID(uint8_t& pldmInstanceID, uint8_t Eid)
539 {
540     constexpr auto service = "xyz.openbmc_project.PLDM";
541     constexpr auto path = "/xyz/openbmc_project/pldm";
542     constexpr auto interface = "xyz.openbmc_project.PLDM.Requester";
543     constexpr auto function = "GetInstanceId";
544 
545     try
546     {
547         // create dbus method
548         auto bus = sdbusplus::bus::new_default();
549         sdbusplus::message_t method =
550             bus.new_method_call(service, path, interface, function);
551 
552         // append endpoint ID
553         method.append(Eid);
554 
555         // request PLDM instance ID
556         auto reply = bus.call(method);
557         reply.read(pldmInstanceID);
558     }
559     catch (const sdbusplus::exception_t& e)
560     {
561         trace::err("get PLDM instance exception");
562         trace::err(e.what());
563         return false;
564     }
565 
566     return true;
567 }
568 
569 /** @brief Determine if power fault was detected */
powerFault()570 bool powerFault()
571 {
572     // power fault based on pgood property
573     int32_t pgood = 0; // assume fault or unknown
574 
575     constexpr auto interface = "org.openbmc.control.Power";
576 
577     DBusService service;
578     DBusPath path;
579 
580     // find a dbus service and object path that implements the interface
581     if (0 == find(interface, path, service))
582     {
583         DBusValue value;
584 
585         // chassis pgood is implemented as a property
586         constexpr auto property = "pgood";
587 
588         if (0 == getProperty(interface, path, service, property, value))
589         {
590             // return value is a variant, int32 == 1 for pgood OK
591             pgood = std::get<int32_t>(value);
592         }
593     }
594 
595     return pgood != 1 ? true : false; // if not pgood then power fault
596 }
597 
598 } // namespace dbus
599 } // namespace util
600