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