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