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