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