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