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