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