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 */ 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 // 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 */ 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 */ 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 */ 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 */ 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 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 = std::format("0x{:02x}{:02x}{:02x}{:02x}", 432 ids.at(0), ids.at(1), ids.at(2), 433 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 */ 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 = bus.new_method_call(service, path, 483 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 */ 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 = bus.new_method_call(service, path, 518 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 MCTP instance associated with endpoint */ 538 bool getMctpInstance(uint8_t& mctpInstance, 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 = bus.new_method_call(service, path, 550 interface, function); 551 552 // append endpoint ID 553 method.append(Eid); 554 555 // request MCTP instance ID 556 auto reply = bus.call(method); 557 reply.read(mctpInstance); 558 } 559 catch (const sdbusplus::exception_t& e) 560 { 561 trace::err("get MCTP 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 */ 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