blob: e0fcd5ca0a3f6979174dfa66d41247749a142da9 [file] [log] [blame]
Caleb Palmer626270a2022-02-21 11:05:08 -06001#include <fmt/format.h>
2
Ben Tyner324234b2021-06-28 17:01:17 -05003#include <util/dbus.hpp>
4#include <util/trace.hpp>
Ben Tynerfe2c50d2021-07-23 13:38:53 -05005#include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
Ben Tyner324234b2021-06-28 17:01:17 -05006
7namespace util
8{
Ben Tyner324234b2021-06-28 17:01:17 -05009namespace dbus
10{
Ben Tyner324234b2021-06-28 17:01:17 -050011//------------------------------------------------------------------------------
12
Patrick Williams27dd6362023-05-10 07:51:20 -050013constexpr auto objectMapperService = "xyz.openbmc_project.ObjectMapper";
14constexpr auto objectMapperPath = "/xyz/openbmc_project/object_mapper";
Ben Tyner324234b2021-06-28 17:01:17 -050015constexpr auto objectMapperInterface = "xyz.openbmc_project.ObjectMapper";
16
Ben Tyner88b10092022-12-14 20:43:50 -060017constexpr uint8_t terminusIdZero = 0;
18
Ben Tyner324234b2021-06-28 17:01:17 -050019/** @brief Find the path and service that implements the given interface */
20int 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
Ben Tyner324234b2021-06-28 17:01:17 -050027 try
28 {
Ben Tyner659e65c2021-07-21 09:49:35 -050029 constexpr auto function = "GetSubTree";
30
Ben Tyner324234b2021-06-28 17:01:17 -050031 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());
Patrick Williams27dd6362023-05-10 07:51:20 -050047 o_path = object.first; // return path
48 o_service = object.second.begin()->first; // return service
Ben Tyner324234b2021-06-28 17:01:17 -050049
Patrick Williams27dd6362023-05-10 07:51:20 -050050 rc = 0; // success
Ben Tyner324234b2021-06-28 17:01:17 -050051 }
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 */
64int 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
Ben Tyner324234b2021-06-28 17:01:17 -050071 try
72 {
Ben Tyner659e65c2021-07-21 09:49:35 -050073 constexpr auto function = "GetObject";
74
Ben Tyner324234b2021-06-28 17:01:17 -050075 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 */
107int 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
Ben Tyner324234b2021-06-28 17:01:17 -0500115 try
116 {
Ben Tyner659e65c2021-07-21 09:49:35 -0500117 constexpr auto interface = "org.freedesktop.DBus.Properties";
Patrick Williams27dd6362023-05-10 07:51:20 -0500118 constexpr auto function = "Get";
Ben Tyner659e65c2021-07-21 09:49:35 -0500119
Ben Tyner324234b2021-06-28 17:01:17 -0500120 // 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 */
143std::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
Ben Tyner93067162021-07-23 10:39:30 -0500171/** @brief Transition the host state */
172void 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
Andrew Geissler1ff926e2023-01-26 08:14:22 -0700185 // 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
Patrick Williams27dd6362023-05-10 07:51:20 -0500194 auto bus = sdbusplus::bus::new_system();
Ben Tyner93067162021-07-23 10:39:30 -0500195 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
Ben Tyner39fcf652021-10-19 20:38:29 -0500212/** @brief Read state of autoRebootEnabled property via dbus */
Ben Tynerffb48672021-07-23 12:29:03 -0500213bool autoRebootEnabled()
214{
Ben Tyner39fcf652021-10-19 20:38:29 -0500215 // Assume true in case autoRebootEnbabled property is not available
216 bool autoReboot = true;
Ben Tynerffb48672021-07-23 12:29:03 -0500217
218 constexpr auto interface = "xyz.openbmc_project.Control.Boot.RebootPolicy";
219
Ben Tyner39fcf652021-10-19 20:38:29 -0500220 DBusService service; // will find this
221 DBusPath path; // will find this
Ben Tynerffb48672021-07-23 12:29:03 -0500222
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
Ben Tynerfe2c50d2021-07-23 13:38:53 -0500241/** @brief Get the running state of the host */
242HostRunningState 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) ||
Ben Tynerfe2c50d2021-07-23 13:38:53 -0500273 (stage == BootProgress::OSRunning))
274 {
275 host = HostRunningState::Started;
276 }
277 else
278 {
279 host = HostRunningState::NotStarted;
280 }
281 }
282 }
283
Andrew Geissler1ff926e2023-01-26 08:14:22 -0700284 // 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
Ben Tynerfe2c50d2021-07-23 13:38:53 -0500310 return host;
311}
312
Ben Tyner39fcf652021-10-19 20:38:29 -0500313/** @brief Read state of dumpPolicyEnabled property via dbus */
314bool 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";
Patrick Williams27dd6362023-05-10 07:51:20 -0500320 constexpr auto path = "/xyz/openbmc_project/dump/system_dump_policy";
Ben Tyner39fcf652021-10-19 20:38:29 -0500321
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
Ben Tyner13159682022-02-16 14:55:38 -0600342/** @brief Create a PEL */
343uint32_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";
Patrick Williams27dd6362023-05-10 07:51:20 -0500352 constexpr auto path = "/xyz/openbmc_project/logging";
Ben Tyner13159682022-02-16 14:55:38 -0600353
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();
Patrick Williamse212fb02022-07-22 19:26:57 -0500368 sdbusplus::message_t method =
Ben Tyner13159682022-02-16 14:55:38 -0600369 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
Caleb Palmer626270a2022-02-21 11:05:08 -0600394MachineType 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 try
428 {
429 // Format the vector into a single hex string to compare to.
Patrick Williams27dd6362023-05-10 07:51:20 -0500430 std::string hexId = fmt::format("0x{:02x}{:02x}{:02x}{:02x}",
431 ids.at(0), ids.at(1), ids.at(2),
432 ids.at(3));
Caleb Palmer626270a2022-02-21 11:05:08 -0600433
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 };
441
442 machineType = typeMap.at(hexId);
443 }
444 catch (const std::out_of_range& e)
445 {
446 trace::err("Out of range exception caught from returned "
447 "machine ID.");
448 for (const auto& id : ids)
449 {
450 trace::err("Returned Machine ID value: 0x%x", id);
451 }
452 throw;
453 }
454 }
455 }
456 else
457 {
458 throw std::invalid_argument(
459 "Unable to find dbus service to get machine type.");
460 }
461
462 return machineType;
463}
464
Ben Tyner88b10092022-12-14 20:43:50 -0600465/** @brief Get list of state effecter PDRs */
466bool getStateEffecterPdrs(std::vector<std::vector<uint8_t>>& pdrList,
467 uint16_t stateSetId)
468{
Patrick Williams27dd6362023-05-10 07:51:20 -0500469 constexpr auto service = "xyz.openbmc_project.PLDM";
470 constexpr auto path = "/xyz/openbmc_project/pldm";
Ben Tyner88b10092022-12-14 20:43:50 -0600471 constexpr auto interface = "xyz.openbmc_project.PLDM.PDR";
Patrick Williams27dd6362023-05-10 07:51:20 -0500472 constexpr auto function = "FindStateEffecterPDR";
Ben Tyner324234b2021-06-28 17:01:17 -0500473
Ben Tyner88b10092022-12-14 20:43:50 -0600474 constexpr uint16_t PLDM_ENTITY_PROC = 135;
475
476 try
477 {
478 // create dbus method
479 auto bus = sdbusplus::bus::new_default();
Patrick Williams27dd6362023-05-10 07:51:20 -0500480 sdbusplus::message_t method = bus.new_method_call(service, path,
481 interface, function);
Ben Tyner88b10092022-12-14 20:43:50 -0600482
483 // append additional method data
484 method.append(terminusIdZero, PLDM_ENTITY_PROC, stateSetId);
485
486 // request PDRs
487 auto reply = bus.call(method);
488 reply.read(pdrList);
489 }
490 catch (const sdbusplus::exception_t& e)
491 {
492 trace::err("failed to find state effecter PDRs");
493 trace::err(e.what());
494 return false;
495 }
496
497 return true;
498}
499
500/** @brief Get list of state sensor PDRs */
501bool getStateSensorPdrs(std::vector<std::vector<uint8_t>>& pdrList,
502 uint16_t stateSetId)
503{
Patrick Williams27dd6362023-05-10 07:51:20 -0500504 constexpr auto service = "xyz.openbmc_project.PLDM";
505 constexpr auto path = "/xyz/openbmc_project/pldm";
Ben Tyner88b10092022-12-14 20:43:50 -0600506 constexpr auto interface = "xyz.openbmc_project.PLDM.PDR";
Patrick Williams27dd6362023-05-10 07:51:20 -0500507 constexpr auto function = "FindStateSensorPDR";
Ben Tyner88b10092022-12-14 20:43:50 -0600508
509 constexpr uint16_t PLDM_ENTITY_PROC = 135;
510
511 try
512 {
513 // create dbus method
514 auto bus = sdbusplus::bus::new_default();
Patrick Williams27dd6362023-05-10 07:51:20 -0500515 sdbusplus::message_t method = bus.new_method_call(service, path,
516 interface, function);
Ben Tyner88b10092022-12-14 20:43:50 -0600517
518 // append additional method data
519 method.append(terminusIdZero, PLDM_ENTITY_PROC, stateSetId);
520
521 // request PDRs
522 auto reply = bus.call(method);
523 reply.read(pdrList);
524 }
525 catch (const sdbusplus::exception_t& e)
526 {
527 trace::err("failed to find state sensor PDRs");
528 trace::err(e.what());
529 return false;
530 }
531
532 return true;
533}
534
535/** @brief Get MCTP instance associated with endpoint */
536bool getMctpInstance(uint8_t& mctpInstance, uint8_t Eid)
537{
Patrick Williams27dd6362023-05-10 07:51:20 -0500538 constexpr auto service = "xyz.openbmc_project.PLDM";
539 constexpr auto path = "/xyz/openbmc_project/pldm";
Ben Tyner88b10092022-12-14 20:43:50 -0600540 constexpr auto interface = "xyz.openbmc_project.PLDM.Requester";
Patrick Williams27dd6362023-05-10 07:51:20 -0500541 constexpr auto function = "GetInstanceId";
Ben Tyner88b10092022-12-14 20:43:50 -0600542
543 try
544 {
545 // create dbus method
546 auto bus = sdbusplus::bus::new_default();
Patrick Williams27dd6362023-05-10 07:51:20 -0500547 sdbusplus::message_t method = bus.new_method_call(service, path,
548 interface, function);
Ben Tyner88b10092022-12-14 20:43:50 -0600549
550 // append endpoint ID
551 method.append(Eid);
552
553 // request MCTP instance ID
554 auto reply = bus.call(method);
555 reply.read(mctpInstance);
556 }
557 catch (const sdbusplus::exception_t& e)
558 {
559 trace::err("get MCTP instance exception");
560 trace::err(e.what());
561 return false;
562 }
563
564 return true;
565}
566
Ben Tyner2b26b2b2022-12-15 15:42:02 -0600567/** @brief Determine if power fault was detected */
568bool powerFault()
569{
570 // power fault based on pgood property
571 int32_t pgood = 0; // assume fault or unknown
572
573 constexpr auto interface = "org.openbmc.control.Power";
574
575 DBusService service;
576 DBusPath path;
577
578 // find a dbus service and object path that implements the interface
579 if (0 == find(interface, path, service))
580 {
581 DBusValue value;
582
583 // chassis pgood is implemented as a property
584 constexpr auto property = "pgood";
585
586 if (0 == getProperty(interface, path, service, property, value))
587 {
588 // return value is a variant, int32 == 1 for pgood OK
589 pgood = std::get<int32_t>(value);
590 }
591 }
592
593 return pgood != 1 ? true : false; // if not pgood then power fault
594}
595
Ben Tyner88b10092022-12-14 20:43:50 -0600596} // namespace dbus
Ben Tyner324234b2021-06-28 17:01:17 -0500597} // namespace util