1 // SPDX-License-Identifier: Apache-2.0 2 3 /**@file functions.cpp*/ 4 5 #include "config.h" 6 7 #include "functions.hpp" 8 9 #include <nlohmann/json.hpp> 10 #include <phosphor-logging/log.hpp> 11 #include <sdbusplus/bus.hpp> 12 #include <sdbusplus/bus/match.hpp> 13 #include <sdbusplus/exception.hpp> 14 #include <sdbusplus/message.hpp> 15 #include <sdeventplus/event.hpp> 16 #include <xyz/openbmc_project/Common/error.hpp> 17 18 #include <filesystem> 19 #include <fstream> 20 #include <functional> 21 #include <iostream> 22 #include <map> 23 #include <memory> 24 #include <string> 25 #include <variant> 26 #include <vector> 27 28 namespace functions 29 { 30 namespace process_hostfirmware 31 { 32 33 using namespace phosphor::logging; 34 using InterfacesPropertiesMap = 35 std::map<std::string, 36 std::map<std::string, std::variant<std::vector<std::string>>>>; 37 using ManagedObjectType = 38 std::map<sdbusplus::message::object_path, InterfacesPropertiesMap>; 39 40 /** 41 * @brief Returns the managed objects for a given service 42 */ 43 ManagedObjectType getManagedObjects(sdbusplus::bus_t& bus, 44 const std::string& service, 45 const std::string& managerPath) 46 47 { 48 auto method = bus.new_method_call(service.c_str(), managerPath.c_str(), 49 "org.freedesktop.DBus.ObjectManager", 50 "GetManagedObjects"); 51 52 ManagedObjectType objects; 53 54 try 55 { 56 auto reply = bus.call(method); 57 reply.read(objects); 58 } 59 catch (const sdbusplus::exception_t& e) 60 { 61 return ManagedObjectType{}; 62 } 63 return objects; 64 } 65 66 /** 67 * @brief Issue callbacks safely 68 * 69 * std::function can be empty, so this wrapper method checks for that prior to 70 * calling it to avoid std::bad_function_call 71 * 72 * @tparam Sig the types of the std::function arguments 73 * @tparam Args the deduced argument types 74 * @param[in] callback the callback being wrapped 75 * @param[in] args the callback arguments 76 */ 77 template <typename... Sig, typename... Args> 78 void makeCallback(const std::function<void(Sig...)>& callback, Args&&... args) 79 { 80 if (callback) 81 { 82 callback(std::forward<Args>(args)...); 83 } 84 } 85 86 /** 87 * @brief Get file extensions for IBMCompatibleSystem 88 * 89 * IBM host firmware can be deployed as blobs (files) in a filesystem. Host 90 * firmware blobs for different values of 91 * xyz.openbmc_project.Configuration.IBMCompatibleSystem are packaged with 92 * different filename extensions. getExtensionsForIbmCompatibleSystem 93 * maintains the mapping from a given value of 94 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to an array of 95 * filename extensions. 96 * 97 * If a mapping is found getExtensionsForIbmCompatibleSystem returns true and 98 * the extensions parameter is reset with the map entry. If no mapping is 99 * found getExtensionsForIbmCompatibleSystem returns false and extensions is 100 * unmodified. 101 * 102 * @param[in] extensionMap a map of 103 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob 104 * file extensions. 105 * @param[in] ibmCompatibleSystem The names property of an instance of 106 * xyz.openbmc_project.Configuration.IBMCompatibleSystem 107 * @param[out] extentions the host firmware blob file extensions 108 * @return true if an entry was found, otherwise false 109 */ 110 bool getExtensionsForIbmCompatibleSystem( 111 const std::map<std::string, std::vector<std::string>>& extensionMap, 112 const std::vector<std::string>& ibmCompatibleSystem, 113 std::vector<std::string>& extensions) 114 { 115 for (const auto& system : ibmCompatibleSystem) 116 { 117 auto extensionMapIterator = extensionMap.find(system); 118 if (extensionMapIterator != extensionMap.end()) 119 { 120 extensions = extensionMapIterator->second; 121 return true; 122 } 123 } 124 125 return false; 126 } 127 128 /** 129 * @brief Write host firmware well-known name 130 * 131 * A wrapper around std::filesystem::create_symlink that avoids EEXIST by 132 * deleting any pre-existing file. 133 * 134 * @param[in] linkTarget The link target argument to 135 * std::filesystem::create_symlink 136 * @param[in] linkPath The link path argument to std::filesystem::create_symlink 137 * @param[in] errorCallback A callback made in the event of filesystem errors. 138 */ 139 void writeLink(const std::filesystem::path& linkTarget, 140 const std::filesystem::path& linkPath, 141 const ErrorCallbackType& errorCallback) 142 { 143 std::error_code ec; 144 145 // remove files with the same name as the symlink to be created, 146 // otherwise symlink will fail with EEXIST. 147 if (!std::filesystem::remove(linkPath, ec)) 148 { 149 if (ec) 150 { 151 makeCallback(errorCallback, linkPath, ec); 152 return; 153 } 154 } 155 156 std::filesystem::create_symlink(linkTarget, linkPath, ec); 157 if (ec) 158 { 159 makeCallback(errorCallback, linkPath, ec); 160 return; 161 } 162 } 163 164 /** 165 * @brief Find host firmware blob files that need well-known names 166 * 167 * The IBM host firmware runtime looks for data and/or additional code while 168 * bootstraping in files with well-known names. findLinks uses the provided 169 * extensions argument to find host firmware blob files that require a 170 * well-known name. When a blob is found, issue the provided callback 171 * (typically a function that will write a symlink). 172 * 173 * @param[in] hostFirmwareDirectory The directory in which findLinks should 174 * look for host firmware blob files that need well-known names. 175 * @param[in] extentions The extensions of the firmware blob files denote a 176 * host firmware blob file requires a well-known name. 177 * @param[in] errorCallback A callback made in the event of filesystem errors. 178 * @param[in] linkCallback A callback made when host firmware blob files 179 * needing a well known name are found. 180 */ 181 void findLinks(const std::filesystem::path& hostFirmwareDirectory, 182 const std::vector<std::string>& extensions, 183 const ErrorCallbackType& errorCallback, 184 const LinkCallbackType& linkCallback) 185 { 186 std::error_code ec; 187 std::filesystem::directory_iterator directoryIterator(hostFirmwareDirectory, 188 ec); 189 if (ec) 190 { 191 makeCallback(errorCallback, hostFirmwareDirectory, ec); 192 return; 193 } 194 195 // Create a symlink for pnor.toc 196 static const auto tocLid = "81e00994.lid"; 197 auto tocLidPath = hostFirmwareDirectory / tocLid; 198 if (std::filesystem::exists(tocLidPath)) 199 { 200 static const auto tocName = "pnor.toc"; 201 auto tocLinkPath = hostFirmwareDirectory / tocName; 202 makeCallback(linkCallback, tocLid, tocLinkPath, errorCallback); 203 } 204 205 for (; directoryIterator != std::filesystem::end(directoryIterator); 206 directoryIterator.increment(ec)) 207 { 208 const auto& file = directoryIterator->path(); 209 if (ec) 210 { 211 makeCallback(errorCallback, file, ec); 212 // quit here if the increment call failed otherwise the loop may 213 // never finish 214 break; 215 } 216 217 if (std::find(extensions.begin(), extensions.end(), file.extension()) == 218 extensions.end()) 219 { 220 // this file doesn't have an extension or doesn't match any of the 221 // provided extensions. 222 continue; 223 } 224 225 auto linkPath(file.parent_path().append( 226 static_cast<const std::string&>(file.stem()))); 227 228 makeCallback(linkCallback, file.filename(), linkPath, errorCallback); 229 } 230 } 231 232 /** 233 * @brief Parse the elements json file and construct a string with the data to 234 * be used to update the bios attribute table. 235 * 236 * @param[in] elementsJsonFilePath - The path to the host firmware json file. 237 * @param[in] extensions - The extensions of the firmware blob files. 238 */ 239 std::string getBiosAttrStr(const std::filesystem::path& elementsJsonFilePath, 240 const std::vector<std::string>& extensions) 241 { 242 std::string biosAttrStr{}; 243 244 std::ifstream jsonFile(elementsJsonFilePath.c_str()); 245 if (!jsonFile) 246 { 247 return {}; 248 } 249 250 std::map<std::string, std::string> attr; 251 auto data = nlohmann::json::parse(jsonFile, nullptr, false); 252 if (data.is_discarded()) 253 { 254 log<level::ERR>("Error parsing JSON file", 255 entry("FILE=%s", elementsJsonFilePath.c_str())); 256 return {}; 257 } 258 259 // .get requires a non-const iterator 260 for (auto& iter : data["lids"]) 261 { 262 std::string name{}; 263 std::string lid{}; 264 265 try 266 { 267 name = iter["element_name"].get<std::string>(); 268 lid = iter["short_lid_name"].get<std::string>(); 269 } 270 catch (const std::exception& e) 271 { 272 // Possibly the element or lid name field was not found 273 log<level::ERR>("Error reading JSON field", 274 entry("FILE=%s", elementsJsonFilePath.c_str()), 275 entry("ERROR=%s", e.what())); 276 continue; 277 } 278 279 // The elements with the ipl extension have higher priority. Therefore 280 // Use operator[] to overwrite value if an entry for it already exists, 281 // and create a second entry with key name element_RT to specify it as 282 // a runtime element. 283 // Ex: if the JSON contains an entry A.P10 with lid name X, it'll create 284 // and try A=X. If the JSON also contained an entry A.P10.iplTime with 285 // lid name Y, the A entry would be overwritten to be A=Y and a second 286 // entry A_RT=X would be created. 287 constexpr auto iplExtension = ".iplTime"; 288 constexpr auto runtimeSuffix = "_RT"; 289 std::filesystem::path path(name); 290 if (path.extension() == iplExtension) 291 { 292 // Some elements have an additional extension, ex: .P10.iplTime 293 // Strip off the ipl extension with stem(), then check if there is 294 // an additional extension with extension(). 295 if (!path.stem().extension().empty()) 296 { 297 // Check if the extension matches the extensions for this system 298 if (std::find(extensions.begin(), extensions.end(), 299 path.stem().extension()) == extensions.end()) 300 { 301 continue; 302 } 303 } 304 // Get the element name without extensions by calling stem() twice 305 // since stem() returns the base name if no periods are found. 306 // Therefore both "element.P10" and "element.P10.iplTime" would 307 // become "element". 308 auto keyName = path.stem().stem(); 309 auto attrIt = attr.find(keyName); 310 if (attrIt != attr.end()) 311 { 312 // Copy the existing entry to a runtime entry 313 auto runtimeKeyName = keyName.string() + runtimeSuffix; 314 attr.insert({runtimeKeyName, attrIt->second}); 315 } 316 // Overwrite the exsiting element with the ipl entry 317 attr[keyName] = lid; 318 continue; 319 } 320 321 // Process all other extensions. The extension should match the list of 322 // supported extensions for this system. Use .insert() to only add 323 // entries that do not exist, so to not overwrite the values that may 324 // had been added that had the ipl extension. 325 if (std::find(extensions.begin(), extensions.end(), path.extension()) != 326 extensions.end()) 327 { 328 auto keyName = path.stem(); 329 auto attrIt = attr.find(keyName); 330 if (attrIt != attr.end()) 331 { 332 // The existing entry is an ipl entry, therefore create this 333 // entry as a runtime one. 334 auto runtimeKeyName = keyName.string() + runtimeSuffix; 335 attr.insert({runtimeKeyName, lid}); 336 } 337 else 338 { 339 attr.insert({path.stem(), lid}); 340 } 341 } 342 } 343 for (const auto& a : attr) 344 { 345 // Build the bios attribute string with format: 346 // "element1=lid1,element2=lid2,elementN=lidN," 347 biosAttrStr += a.first + "=" + a.second + ","; 348 349 // Create symlinks from the hostfw elements to their corresponding 350 // lid files if they don't exist 351 auto elementFilePath = std::filesystem::path("/media/hostfw/running") / 352 a.first; 353 if (!std::filesystem::exists(elementFilePath)) 354 { 355 std::error_code ec; 356 auto lidName = a.second + ".lid"; 357 std::filesystem::create_symlink(lidName, elementFilePath, ec); 358 if (ec) 359 { 360 log<level::ERR>("Error creating symlink", 361 entry("TARGET=%s", lidName.c_str()), 362 entry("LINK=%s", elementFilePath.c_str())); 363 } 364 } 365 } 366 367 // Delete the last comma of the bios attribute string 368 if (biosAttrStr.back() == ',') 369 { 370 return biosAttrStr.substr(0, biosAttrStr.length() - 1); 371 } 372 373 return biosAttrStr; 374 } 375 376 /** 377 * @brief Set the bios attribute table with details of the host firmware data 378 * for this system. 379 * 380 * @param[in] elementsJsonFilePath - The path to the host firmware json file. 381 * @param[in] extentions - The extensions of the firmware blob files. 382 */ 383 void setBiosAttr(const std::filesystem::path& elementsJsonFilePath, 384 const std::vector<std::string>& extensions) 385 { 386 auto biosAttrStr = getBiosAttrStr(elementsJsonFilePath, extensions); 387 388 constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager"; 389 constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager"; 390 constexpr auto dbusAttrName = "hb_lid_ids"; 391 constexpr auto dbusAttrType = 392 "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String"; 393 394 using PendingAttributesType = std::vector<std::pair< 395 std::string, std::tuple<std::string, std::variant<std::string>>>>; 396 PendingAttributesType pendingAttributes; 397 pendingAttributes.emplace_back(std::make_pair( 398 dbusAttrName, std::make_tuple(dbusAttrType, biosAttrStr))); 399 400 auto bus = sdbusplus::bus::new_default(); 401 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 402 MAPPER_INTERFACE, "GetObject"); 403 method.append(biosConfigPath, std::vector<std::string>({biosConfigIntf})); 404 std::vector<std::pair<std::string, std::vector<std::string>>> response; 405 try 406 { 407 auto reply = bus.call(method); 408 reply.read(response); 409 if (response.empty()) 410 { 411 log<level::INFO>("Error reading mapper response", 412 entry("PATH=%s", biosConfigPath), 413 entry("INTERFACE=%s", biosConfigIntf)); 414 throw sdbusplus::xyz::openbmc_project::Common::Error:: 415 InternalFailure(); 416 } 417 auto method = bus.new_method_call((response.begin()->first).c_str(), 418 biosConfigPath, 419 SYSTEMD_PROPERTY_INTERFACE, "Set"); 420 method.append(biosConfigIntf, "PendingAttributes", 421 std::variant<PendingAttributesType>(pendingAttributes)); 422 bus.call(method); 423 } 424 catch (const sdbusplus::exception_t& e) 425 { 426 log<level::INFO>("Error setting the bios attribute", 427 entry("ERROR=%s", e.what()), 428 entry("ATTRIBUTE=%s", dbusAttrName)); 429 throw; 430 } 431 } 432 433 /** 434 * @brief Make callbacks on 435 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances. 436 * 437 * Look for an instance of 438 * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided 439 * argument and if found, issue the provided callback. 440 * 441 * @param[in] interfacesAndProperties the interfaces in which to look for an 442 * instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem 443 * @param[in] callback the user callback to make if 444 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in 445 * interfacesAndProperties 446 * @return true if interfacesAndProperties contained an instance of 447 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise 448 */ 449 bool maybeCall(const std::map<std::string, 450 std::map<std::string, 451 std::variant<std::vector<std::string>>>>& 452 interfacesAndProperties, 453 const MaybeCallCallbackType& callback) 454 { 455 using namespace std::string_literals; 456 457 static const auto interfaceName = 458 "xyz.openbmc_project.Configuration.IBMCompatibleSystem"s; 459 auto interfaceIterator = interfacesAndProperties.find(interfaceName); 460 if (interfaceIterator == interfacesAndProperties.cend()) 461 { 462 // IBMCompatibleSystem interface not found, so instruct the caller to 463 // keep waiting or try again later. 464 return false; 465 } 466 auto propertyIterator = interfaceIterator->second.find("Names"s); 467 if (propertyIterator == interfaceIterator->second.cend()) 468 { 469 // The interface exists but the property doesn't. This is a bug in the 470 // IBMCompatibleSystem implementation. The caller should not try 471 // again. 472 std::cerr << "Names property not implemented on " << interfaceName 473 << "\n"; 474 return true; 475 } 476 477 const auto& ibmCompatibleSystem = 478 std::get<std::vector<std::string>>(propertyIterator->second); 479 if (callback) 480 { 481 try 482 { 483 callback(ibmCompatibleSystem); 484 } 485 catch (const sdbusplus::exception_t& e) 486 { 487 return false; 488 } 489 } 490 491 // IBMCompatibleSystem found and callback issued. 492 return true; 493 } 494 495 /** 496 * @brief Make callbacks on 497 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances. 498 * 499 * Look for an instance of 500 * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided 501 * argument and if found, issue the provided callback. 502 * 503 * @param[in] message the DBus message in which to look for an instance of 504 * xyz.openbmc_project.Configuration.IBMCompatibleSystem 505 * @param[in] callback the user callback to make if 506 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in 507 * message 508 * @return true if message contained an instance of 509 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise 510 */ 511 bool maybeCallMessage(sdbusplus::message_t& message, 512 const MaybeCallCallbackType& callback) 513 { 514 std::map<std::string, 515 std::map<std::string, std::variant<std::vector<std::string>>>> 516 interfacesAndProperties; 517 sdbusplus::message::object_path _; 518 message.read(_, interfacesAndProperties); 519 return maybeCall(interfacesAndProperties, callback); 520 } 521 522 /** 523 * @brief Determine system support for host firmware well-known names. 524 * 525 * Using the provided extensionMap and 526 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if 527 * well-known names for host firmare blob files are necessary and if so, create 528 * them. 529 * 530 * @param[in] extensionMap a map of 531 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob 532 * file extensions. 533 * @param[in] hostFirmwareDirectory The directory in which findLinks should 534 * look for host firmware blob files that need well-known names. 535 * @param[in] ibmCompatibleSystem The names property of an instance of 536 * xyz.openbmc_project.Configuration.IBMCompatibleSystem 537 * @param[in] errorCallback A callback made in the event of filesystem errors. 538 */ 539 void maybeMakeLinks( 540 const std::map<std::string, std::vector<std::string>>& extensionMap, 541 const std::filesystem::path& hostFirmwareDirectory, 542 const std::vector<std::string>& ibmCompatibleSystem, 543 const ErrorCallbackType& errorCallback) 544 { 545 std::vector<std::string> extensions; 546 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem, 547 extensions)) 548 { 549 findLinks(hostFirmwareDirectory, extensions, errorCallback, writeLink); 550 } 551 } 552 553 /** 554 * @brief Determine system support for updating the bios attribute table. 555 * 556 * Using the provided extensionMap and 557 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if the bios 558 * attribute table needs to be updated. 559 * 560 * @param[in] extensionMap a map of 561 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob 562 * file extensions. 563 * @param[in] elementsJsonFilePath The file path to the json file 564 * @param[in] ibmCompatibleSystem The names property of an instance of 565 * xyz.openbmc_project.Configuration.IBMCompatibleSystem 566 */ 567 void maybeSetBiosAttr( 568 const std::map<std::string, std::vector<std::string>>& extensionMap, 569 const std::filesystem::path& elementsJsonFilePath, 570 const std::vector<std::string>& ibmCompatibleSystem) 571 { 572 std::vector<std::string> extensions; 573 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem, 574 extensions)) 575 { 576 try 577 { 578 setBiosAttr(elementsJsonFilePath, extensions); 579 } 580 catch (const sdbusplus::exception_t& e) 581 { 582 throw; 583 } 584 } 585 } 586 587 /** 588 * @brief process host firmware 589 * 590 * Allocate a callback context and register for DBus.ObjectManager Interfaces 591 * added signals from entity manager. 592 * 593 * Check the current entity manager object tree for a 594 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instance (entity 595 * manager will be dbus activated if it is not running). If one is found, 596 * determine if symlinks need to be created and create them. Instruct the 597 * program event loop to exit. 598 * 599 * If no instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is 600 * found return the callback context to main, where the program will sleep 601 * until the callback is invoked one or more times and instructs the program 602 * event loop to exit when 603 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is added. 604 * 605 * @param[in] bus a DBus client connection 606 * @param[in] extensionMap a map of 607 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob 608 * file extensions. 609 * @param[in] hostFirmwareDirectory The directory in which processHostFirmware 610 * should look for blob files. 611 * @param[in] errorCallback A callback made in the event of filesystem errors. 612 * @param[in] loop a program event loop 613 * @return nullptr if an instance of 614 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found, otherwise a 615 * pointer to an sdbusplus match object. 616 */ 617 std::shared_ptr<void> processHostFirmware( 618 sdbusplus::bus_t& bus, 619 std::map<std::string, std::vector<std::string>> extensionMap, 620 std::filesystem::path hostFirmwareDirectory, 621 ErrorCallbackType errorCallback, sdeventplus::Event& loop) 622 { 623 // ownership of extensionMap, hostFirmwareDirectory and errorCallback can't 624 // be transfered to the match callback because they are needed in the non 625 // async part of this function below, so they need to be moved to the heap. 626 auto pExtensionMap = 627 std::make_shared<decltype(extensionMap)>(std::move(extensionMap)); 628 auto pHostFirmwareDirectory = 629 std::make_shared<decltype(hostFirmwareDirectory)>( 630 std::move(hostFirmwareDirectory)); 631 auto pErrorCallback = 632 std::make_shared<decltype(errorCallback)>(std::move(errorCallback)); 633 634 // register for a callback in case the IBMCompatibleSystem interface has 635 // not yet been published by entity manager. 636 auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match_t>( 637 bus, 638 sdbusplus::bus::match::rules::interfacesAdded() + 639 sdbusplus::bus::match::rules::sender( 640 "xyz.openbmc_project.EntityManager"), 641 [pExtensionMap, pHostFirmwareDirectory, pErrorCallback, 642 &loop](auto& message) { 643 // bind the extension map, host firmware directory, and error 644 // callback to the maybeMakeLinks function. 645 auto maybeMakeLinksWithArgsBound = 646 std::bind(maybeMakeLinks, std::cref(*pExtensionMap), 647 std::cref(*pHostFirmwareDirectory), std::placeholders::_1, 648 std::cref(*pErrorCallback)); 649 650 // if the InterfacesAdded message contains an an instance of 651 // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to 652 // see if links are necessary on this system and if so, create 653 // them. 654 if (maybeCallMessage(message, maybeMakeLinksWithArgsBound)) 655 { 656 // The IBMCompatibleSystem interface was found and the links 657 // were created if applicable. Instruct the event loop / 658 // subcommand to exit. 659 loop.exit(0); 660 } 661 }); 662 663 // now that we'll get a callback in the event of an InterfacesAdded signal 664 // (potentially containing 665 // xyz.openbmc_project.Configuration.IBMCompatibleSystem), activate entity 666 // manager if it isn't running and enumerate its objects 667 auto getManagedObjects = bus.new_method_call( 668 "xyz.openbmc_project.EntityManager", "/xyz/openbmc_project/inventory", 669 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 670 std::map<std::string, 671 std::map<std::string, std::variant<std::vector<std::string>>>> 672 interfacesAndProperties; 673 std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)> 674 objects; 675 try 676 { 677 auto reply = bus.call(getManagedObjects); 678 reply.read(objects); 679 } 680 catch (const sdbusplus::exception_t& e) 681 { 682 // Error querying the EntityManager interface. Return the match to have 683 // the callback run if/when the interface appears in D-Bus. 684 return interfacesAddedMatch; 685 } 686 687 // bind the extension map, host firmware directory, and error callback to 688 // the maybeMakeLinks function. 689 auto maybeMakeLinksWithArgsBound = 690 std::bind(maybeMakeLinks, std::cref(*pExtensionMap), 691 std::cref(*pHostFirmwareDirectory), std::placeholders::_1, 692 std::cref(*pErrorCallback)); 693 694 for (const auto& pair : objects) 695 { 696 std::tie(std::ignore, interfacesAndProperties) = pair; 697 // if interfacesAndProperties contains an an instance of 698 // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to see 699 // if links are necessary on this system and if so, create them 700 if (maybeCall(interfacesAndProperties, maybeMakeLinksWithArgsBound)) 701 { 702 // The IBMCompatibleSystem interface is already on the bus and the 703 // links were created if applicable. Instruct the event loop to 704 // exit. 705 loop.exit(0); 706 // The match object isn't needed anymore, so destroy it on return. 707 return nullptr; 708 } 709 } 710 711 // The IBMCompatibleSystem interface has not yet been published. Move 712 // ownership of the match callback to the caller. 713 return interfacesAddedMatch; 714 } 715 716 /** 717 * @brief Update the Bios Attribute Table 718 * 719 * If an instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is 720 * found, update the Bios Attribute Table with the appropriate host firmware 721 * data. 722 * 723 * @param[in] bus - D-Bus client connection. 724 * @param[in] extensionMap - Map of IBMCompatibleSystem names and host firmware 725 * file extensions. 726 * @param[in] elementsJsonFilePath - The Path to the json file 727 * @param[in] loop - Program event loop. 728 * @return nullptr 729 */ 730 std::vector<std::shared_ptr<void>> updateBiosAttrTable( 731 sdbusplus::bus_t& bus, 732 std::map<std::string, std::vector<std::string>> extensionMap, 733 std::filesystem::path elementsJsonFilePath, sdeventplus::Event& loop) 734 { 735 constexpr auto pldmPath = "/xyz/openbmc_project/pldm"; 736 constexpr auto entityManagerServiceName = 737 "xyz.openbmc_project.EntityManager"; 738 739 auto pExtensionMap = 740 std::make_shared<decltype(extensionMap)>(std::move(extensionMap)); 741 auto pElementsJsonFilePath = 742 std::make_shared<decltype(elementsJsonFilePath)>( 743 std::move(elementsJsonFilePath)); 744 745 auto maybeSetAttrWithArgsBound = 746 std::bind(maybeSetBiosAttr, std::cref(*pExtensionMap), 747 std::cref(*pElementsJsonFilePath), std::placeholders::_1); 748 749 std::vector<std::shared_ptr<void>> matches; 750 751 // Entity Manager is needed to get the list of supported extensions. Add a 752 // match to monitor interfaces added in case it's not running yet. 753 matches.emplace_back(std::make_shared<sdbusplus::bus::match_t>( 754 bus, 755 sdbusplus::bus::match::rules::interfacesAdded() + 756 sdbusplus::bus::match::rules::sender( 757 "xyz.openbmc_project.EntityManager"), 758 [pldmPath, pExtensionMap, pElementsJsonFilePath, 759 maybeSetAttrWithArgsBound, &loop](auto& message) { 760 if (maybeCallMessage(message, maybeSetAttrWithArgsBound)) 761 { 762 loop.exit(0); 763 } 764 })); 765 766 // The BIOS attribute table can only be updated if PLDM is running because 767 // PLDM is the one that exposes this property. Add a match to monitor when 768 // the PLDM service starts. 769 matches.emplace_back(std::make_shared<sdbusplus::bus::match_t>( 770 bus, 771 sdbusplus::bus::match::rules::nameOwnerChanged() + 772 sdbusplus::bus::match::rules::arg0namespace( 773 "xyz.openbmc_project.PLDM"), 774 [pExtensionMap, pElementsJsonFilePath, maybeSetAttrWithArgsBound, 775 &loop](auto& message) { 776 std::string name; 777 std::string oldOwner; 778 std::string newOwner; 779 message.read(name, oldOwner, newOwner); 780 781 if (newOwner.empty()) 782 { 783 return; 784 } 785 786 auto bus = sdbusplus::bus::new_default(); 787 InterfacesPropertiesMap interfacesAndProperties; 788 auto objects = getManagedObjects(bus, entityManagerServiceName, 789 "/xyz/openbmc_project/inventory"); 790 for (const auto& pair : objects) 791 { 792 std::tie(std::ignore, interfacesAndProperties) = pair; 793 if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound)) 794 { 795 loop.exit(0); 796 } 797 } 798 })); 799 800 InterfacesPropertiesMap interfacesAndProperties; 801 auto objects = getManagedObjects(bus, entityManagerServiceName, 802 "/xyz/openbmc_project/inventory"); 803 for (const auto& pair : objects) 804 { 805 std::tie(std::ignore, interfacesAndProperties) = pair; 806 if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound)) 807 { 808 loop.exit(0); 809 return {}; 810 } 811 } 812 813 return matches; 814 } 815 816 } // namespace process_hostfirmware 817 } // namespace functions 818