1099543e4SBrad Bishop // SPDX-License-Identifier: Apache-2.0
2099543e4SBrad Bishop 
3099543e4SBrad Bishop /**@file functions.cpp*/
4099543e4SBrad Bishop 
556f538caSAdriana Kobylak #include "config.h"
656f538caSAdriana Kobylak 
7099543e4SBrad Bishop #include "functions.hpp"
8099543e4SBrad Bishop 
9ae0998f1SAdriana Kobylak #include <nlohmann/json.hpp>
1056f538caSAdriana Kobylak #include <phosphor-logging/log.hpp>
11099543e4SBrad Bishop #include <sdbusplus/bus.hpp>
12099543e4SBrad Bishop #include <sdbusplus/bus/match.hpp>
13c79fa915SAdriana Kobylak #include <sdbusplus/exception.hpp>
14099543e4SBrad Bishop #include <sdbusplus/message.hpp>
15099543e4SBrad Bishop #include <sdeventplus/event.hpp>
16099543e4SBrad Bishop 
17099543e4SBrad Bishop #include <filesystem>
18ae0998f1SAdriana Kobylak #include <fstream>
19099543e4SBrad Bishop #include <functional>
20099543e4SBrad Bishop #include <iostream>
21099543e4SBrad Bishop #include <map>
22099543e4SBrad Bishop #include <memory>
23099543e4SBrad Bishop #include <string>
24099543e4SBrad Bishop #include <variant>
25099543e4SBrad Bishop #include <vector>
26099543e4SBrad Bishop 
27099543e4SBrad Bishop namespace functions
28099543e4SBrad Bishop {
29099543e4SBrad Bishop namespace process_hostfirmware
30099543e4SBrad Bishop {
31099543e4SBrad Bishop 
3256f538caSAdriana Kobylak using namespace phosphor::logging;
33ebf67bf7SAdriana Kobylak using InterfacesPropertiesMap =
34ebf67bf7SAdriana Kobylak     std::map<std::string,
35ebf67bf7SAdriana Kobylak              std::map<std::string, std::variant<std::vector<std::string>>>>;
36ebf67bf7SAdriana Kobylak using ManagedObjectType =
37ebf67bf7SAdriana Kobylak     std::map<sdbusplus::message::object_path, InterfacesPropertiesMap>;
3856f538caSAdriana Kobylak 
39099543e4SBrad Bishop /**
40fd4a6088SAdriana Kobylak  * @brief GetObject function to find the service given an object path.
41fd4a6088SAdriana Kobylak  *        It is used to determine if a service is running, so there is no need
42fd4a6088SAdriana Kobylak  *        to specify interfaces as a parameter to constrain the search.
43fd4a6088SAdriana Kobylak  */
44fd4a6088SAdriana Kobylak std::string getObject(sdbusplus::bus::bus& bus, const std::string& path)
45fd4a6088SAdriana Kobylak {
46fd4a6088SAdriana Kobylak     auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
47fd4a6088SAdriana Kobylak                                       MAPPER_BUSNAME, "GetObject");
48fd4a6088SAdriana Kobylak     method.append(path);
49fd4a6088SAdriana Kobylak     std::vector<std::string> interfaces;
50fd4a6088SAdriana Kobylak     method.append(interfaces);
51fd4a6088SAdriana Kobylak 
52fd4a6088SAdriana Kobylak     std::vector<std::pair<std::string, std::vector<std::string>>> response;
53fd4a6088SAdriana Kobylak 
54fd4a6088SAdriana Kobylak     try
55fd4a6088SAdriana Kobylak     {
56fd4a6088SAdriana Kobylak         auto reply = bus.call(method);
57fd4a6088SAdriana Kobylak         reply.read(response);
58fd4a6088SAdriana Kobylak         if (response.empty())
59fd4a6088SAdriana Kobylak         {
60fd4a6088SAdriana Kobylak             return std::string{};
61fd4a6088SAdriana Kobylak         }
62fd4a6088SAdriana Kobylak     }
637b5685d1SPatrick Williams     catch (const sdbusplus::exception::exception& e)
64fd4a6088SAdriana Kobylak     {
65fd4a6088SAdriana Kobylak         return std::string{};
66fd4a6088SAdriana Kobylak     }
67fd4a6088SAdriana Kobylak     return response[0].first;
68fd4a6088SAdriana Kobylak }
69fd4a6088SAdriana Kobylak 
70fd4a6088SAdriana Kobylak /**
71ebf67bf7SAdriana Kobylak  * @brief Returns the managed objects for a given service
72ebf67bf7SAdriana Kobylak  */
73ebf67bf7SAdriana Kobylak ManagedObjectType getManagedObjects(sdbusplus::bus::bus& bus,
74ebf67bf7SAdriana Kobylak                                     const std::string& service)
75ebf67bf7SAdriana Kobylak {
76ebf67bf7SAdriana Kobylak     auto method = bus.new_method_call(service.c_str(), "/",
77ebf67bf7SAdriana Kobylak                                       "org.freedesktop.DBus.ObjectManager",
78ebf67bf7SAdriana Kobylak                                       "GetManagedObjects");
79ebf67bf7SAdriana Kobylak 
80ebf67bf7SAdriana Kobylak     ManagedObjectType objects;
81ebf67bf7SAdriana Kobylak 
82ebf67bf7SAdriana Kobylak     try
83ebf67bf7SAdriana Kobylak     {
84ebf67bf7SAdriana Kobylak         auto reply = bus.call(method);
85ebf67bf7SAdriana Kobylak         reply.read(objects);
86ebf67bf7SAdriana Kobylak     }
877b5685d1SPatrick Williams     catch (const sdbusplus::exception::exception& e)
88ebf67bf7SAdriana Kobylak     {
89ebf67bf7SAdriana Kobylak         return ManagedObjectType{};
90ebf67bf7SAdriana Kobylak     }
91ebf67bf7SAdriana Kobylak     return objects;
92ebf67bf7SAdriana Kobylak }
93ebf67bf7SAdriana Kobylak 
94ebf67bf7SAdriana Kobylak /**
95099543e4SBrad Bishop  * @brief Issue callbacks safely
96099543e4SBrad Bishop  *
97099543e4SBrad Bishop  * std::function can be empty, so this wrapper method checks for that prior to
98099543e4SBrad Bishop  * calling it to avoid std::bad_function_call
99099543e4SBrad Bishop  *
100099543e4SBrad Bishop  * @tparam Sig the types of the std::function arguments
101099543e4SBrad Bishop  * @tparam Args the deduced argument types
102099543e4SBrad Bishop  * @param[in] callback the callback being wrapped
103099543e4SBrad Bishop  * @param[in] args the callback arguments
104099543e4SBrad Bishop  */
105099543e4SBrad Bishop template <typename... Sig, typename... Args>
106099543e4SBrad Bishop void makeCallback(const std::function<void(Sig...)>& callback, Args&&... args)
107099543e4SBrad Bishop {
108099543e4SBrad Bishop     if (callback)
109099543e4SBrad Bishop     {
110099543e4SBrad Bishop         callback(std::forward<Args>(args)...);
111099543e4SBrad Bishop     }
112099543e4SBrad Bishop }
113099543e4SBrad Bishop 
114099543e4SBrad Bishop /**
115099543e4SBrad Bishop  * @brief Get file extensions for IBMCompatibleSystem
116099543e4SBrad Bishop  *
117099543e4SBrad Bishop  * IBM host firmware can be deployed as blobs (files) in a filesystem.  Host
118099543e4SBrad Bishop  * firmware blobs for different values of
119099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem are packaged with
120099543e4SBrad Bishop  * different filename extensions.  getExtensionsForIbmCompatibleSystem
121099543e4SBrad Bishop  * maintains the mapping from a given value of
122099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to an array of
123099543e4SBrad Bishop  * filename extensions.
124099543e4SBrad Bishop  *
125099543e4SBrad Bishop  * If a mapping is found getExtensionsForIbmCompatibleSystem returns true and
126099543e4SBrad Bishop  * the extensions parameter is reset with the map entry.  If no mapping is
127099543e4SBrad Bishop  * found getExtensionsForIbmCompatibleSystem returns false and extensions is
128099543e4SBrad Bishop  * unmodified.
129099543e4SBrad Bishop  *
130099543e4SBrad Bishop  * @param[in] extensionMap a map of
131099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
132099543e4SBrad Bishop  * file extensions.
133099543e4SBrad Bishop  * @param[in] ibmCompatibleSystem The names property of an instance of
134099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem
135099543e4SBrad Bishop  * @param[out] extentions the host firmware blob file extensions
136099543e4SBrad Bishop  * @return true if an entry was found, otherwise false
137099543e4SBrad Bishop  */
138099543e4SBrad Bishop bool getExtensionsForIbmCompatibleSystem(
139099543e4SBrad Bishop     const std::map<std::string, std::vector<std::string>>& extensionMap,
140099543e4SBrad Bishop     const std::vector<std::string>& ibmCompatibleSystem,
141099543e4SBrad Bishop     std::vector<std::string>& extensions)
142099543e4SBrad Bishop {
143099543e4SBrad Bishop     for (const auto& system : ibmCompatibleSystem)
144099543e4SBrad Bishop     {
145099543e4SBrad Bishop         auto extensionMapIterator = extensionMap.find(system);
146099543e4SBrad Bishop         if (extensionMapIterator != extensionMap.end())
147099543e4SBrad Bishop         {
148099543e4SBrad Bishop             extensions = extensionMapIterator->second;
149099543e4SBrad Bishop             return true;
150099543e4SBrad Bishop         }
151099543e4SBrad Bishop     }
152099543e4SBrad Bishop 
153099543e4SBrad Bishop     return false;
154099543e4SBrad Bishop }
155099543e4SBrad Bishop 
156099543e4SBrad Bishop /**
157099543e4SBrad Bishop  * @brief Write host firmware well-known name
158099543e4SBrad Bishop  *
159099543e4SBrad Bishop  * A wrapper around std::filesystem::create_symlink that avoids EEXIST by
160099543e4SBrad Bishop  * deleting any pre-existing file.
161099543e4SBrad Bishop  *
162099543e4SBrad Bishop  * @param[in] linkTarget The link target argument to
163099543e4SBrad Bishop  * std::filesystem::create_symlink
164099543e4SBrad Bishop  * @param[in] linkPath The link path argument to std::filesystem::create_symlink
165099543e4SBrad Bishop  * @param[in] errorCallback A callback made in the event of filesystem errors.
166099543e4SBrad Bishop  */
167099543e4SBrad Bishop void writeLink(const std::filesystem::path& linkTarget,
168099543e4SBrad Bishop                const std::filesystem::path& linkPath,
169099543e4SBrad Bishop                const ErrorCallbackType& errorCallback)
170099543e4SBrad Bishop {
171099543e4SBrad Bishop     std::error_code ec;
172099543e4SBrad Bishop 
173099543e4SBrad Bishop     // remove files with the same name as the symlink to be created,
174099543e4SBrad Bishop     // otherwise symlink will fail with EEXIST.
175099543e4SBrad Bishop     if (!std::filesystem::remove(linkPath, ec))
176099543e4SBrad Bishop     {
177099543e4SBrad Bishop         if (ec)
178099543e4SBrad Bishop         {
179099543e4SBrad Bishop             makeCallback(errorCallback, linkPath, ec);
180099543e4SBrad Bishop             return;
181099543e4SBrad Bishop         }
182099543e4SBrad Bishop     }
183099543e4SBrad Bishop 
184099543e4SBrad Bishop     std::filesystem::create_symlink(linkTarget, linkPath, ec);
185099543e4SBrad Bishop     if (ec)
186099543e4SBrad Bishop     {
187099543e4SBrad Bishop         makeCallback(errorCallback, linkPath, ec);
188099543e4SBrad Bishop         return;
189099543e4SBrad Bishop     }
190099543e4SBrad Bishop }
191099543e4SBrad Bishop 
192099543e4SBrad Bishop /**
193099543e4SBrad Bishop  * @brief Find host firmware blob files that need well-known names
194099543e4SBrad Bishop  *
195099543e4SBrad Bishop  * The IBM host firmware runtime looks for data and/or additional code while
196099543e4SBrad Bishop  * bootstraping in files with well-known names.  findLinks uses the provided
197099543e4SBrad Bishop  * extensions argument to find host firmware blob files that require a
198099543e4SBrad Bishop  * well-known name.  When a blob is found, issue the provided callback
199099543e4SBrad Bishop  * (typically a function that will write a symlink).
200099543e4SBrad Bishop  *
201099543e4SBrad Bishop  * @param[in] hostFirmwareDirectory The directory in which findLinks should
202099543e4SBrad Bishop  * look for host firmware blob files that need well-known names.
203099543e4SBrad Bishop  * @param[in] extentions The extensions of the firmware blob files denote a
204099543e4SBrad Bishop  * host firmware blob file requires a well-known name.
205099543e4SBrad Bishop  * @param[in] errorCallback A callback made in the event of filesystem errors.
206099543e4SBrad Bishop  * @param[in] linkCallback A callback made when host firmware blob files
207099543e4SBrad Bishop  * needing a well known name are found.
208099543e4SBrad Bishop  */
209099543e4SBrad Bishop void findLinks(const std::filesystem::path& hostFirmwareDirectory,
210099543e4SBrad Bishop                const std::vector<std::string>& extensions,
211099543e4SBrad Bishop                const ErrorCallbackType& errorCallback,
212099543e4SBrad Bishop                const LinkCallbackType& linkCallback)
213099543e4SBrad Bishop {
214099543e4SBrad Bishop     std::error_code ec;
215099543e4SBrad Bishop     std::filesystem::directory_iterator directoryIterator(hostFirmwareDirectory,
216099543e4SBrad Bishop                                                           ec);
217099543e4SBrad Bishop     if (ec)
218099543e4SBrad Bishop     {
219099543e4SBrad Bishop         makeCallback(errorCallback, hostFirmwareDirectory, ec);
220099543e4SBrad Bishop         return;
221099543e4SBrad Bishop     }
222099543e4SBrad Bishop 
2234e82bc84SAdriana Kobylak     // Create a symlink for pnor.toc
2244e82bc84SAdriana Kobylak     static const auto tocLid = "81e00994.lid";
2254e82bc84SAdriana Kobylak     auto tocLidPath = hostFirmwareDirectory / tocLid;
2264e82bc84SAdriana Kobylak     if (std::filesystem::exists(tocLidPath))
2274e82bc84SAdriana Kobylak     {
2284e82bc84SAdriana Kobylak         static const auto tocName = "pnor.toc";
2294e82bc84SAdriana Kobylak         auto tocLinkPath = hostFirmwareDirectory / tocName;
2304e82bc84SAdriana Kobylak         makeCallback(linkCallback, tocLid, tocLinkPath, errorCallback);
2314e82bc84SAdriana Kobylak     }
2324e82bc84SAdriana Kobylak 
233099543e4SBrad Bishop     for (; directoryIterator != std::filesystem::end(directoryIterator);
234099543e4SBrad Bishop          directoryIterator.increment(ec))
235099543e4SBrad Bishop     {
236099543e4SBrad Bishop         const auto& file = directoryIterator->path();
237099543e4SBrad Bishop         if (ec)
238099543e4SBrad Bishop         {
239099543e4SBrad Bishop             makeCallback(errorCallback, file, ec);
240099543e4SBrad Bishop             // quit here if the increment call failed otherwise the loop may
241099543e4SBrad Bishop             // never finish
242099543e4SBrad Bishop             break;
243099543e4SBrad Bishop         }
244099543e4SBrad Bishop 
245099543e4SBrad Bishop         if (std::find(extensions.begin(), extensions.end(), file.extension()) ==
246099543e4SBrad Bishop             extensions.end())
247099543e4SBrad Bishop         {
248099543e4SBrad Bishop             // this file doesn't have an extension or doesn't match any of the
249099543e4SBrad Bishop             // provided extensions.
250099543e4SBrad Bishop             continue;
251099543e4SBrad Bishop         }
252099543e4SBrad Bishop 
253099543e4SBrad Bishop         auto linkPath(file.parent_path().append(
254099543e4SBrad Bishop             static_cast<const std::string&>(file.stem())));
255099543e4SBrad Bishop 
256099543e4SBrad Bishop         makeCallback(linkCallback, file.filename(), linkPath, errorCallback);
257099543e4SBrad Bishop     }
258099543e4SBrad Bishop }
259099543e4SBrad Bishop 
260099543e4SBrad Bishop /**
261ae0998f1SAdriana Kobylak  * @brief Parse the elements json file and construct a string with the data to
262ae0998f1SAdriana Kobylak  *        be used to update the bios attribute table.
263ae0998f1SAdriana Kobylak  *
264ae0998f1SAdriana Kobylak  * @param[in] elementsJsonFilePath - The path to the host firmware json file.
265ae0998f1SAdriana Kobylak  * @param[in] extensions - The extensions of the firmware blob files.
26653a27395SAdriana Kobylak  */
267ae0998f1SAdriana Kobylak std::string getBiosAttrStr(const std::filesystem::path& elementsJsonFilePath,
268ae0998f1SAdriana Kobylak                            const std::vector<std::string>& extensions)
26956f538caSAdriana Kobylak {
27056f538caSAdriana Kobylak     std::string biosAttrStr{};
27156f538caSAdriana Kobylak 
272ae0998f1SAdriana Kobylak     std::ifstream jsonFile(elementsJsonFilePath.c_str());
273ae0998f1SAdriana Kobylak     if (!jsonFile)
274ae0998f1SAdriana Kobylak     {
275ae0998f1SAdriana Kobylak         return {};
276ae0998f1SAdriana Kobylak     }
277ae0998f1SAdriana Kobylak 
278ae0998f1SAdriana Kobylak     std::map<std::string, std::string> attr;
279ae0998f1SAdriana Kobylak     auto data = nlohmann::json::parse(jsonFile, nullptr, false);
280ae0998f1SAdriana Kobylak     if (data.is_discarded())
281ae0998f1SAdriana Kobylak     {
282ae0998f1SAdriana Kobylak         log<level::ERR>("Error parsing JSON file",
283ae0998f1SAdriana Kobylak                         entry("FILE=%s", elementsJsonFilePath.c_str()));
284ae0998f1SAdriana Kobylak         return {};
285ae0998f1SAdriana Kobylak     }
286ae0998f1SAdriana Kobylak 
287ae0998f1SAdriana Kobylak     // .get requires a non-const iterator
288ae0998f1SAdriana Kobylak     for (auto& iter : data["lids"])
289ae0998f1SAdriana Kobylak     {
290ae0998f1SAdriana Kobylak         std::string name{};
291ae0998f1SAdriana Kobylak         std::string lid{};
292ae0998f1SAdriana Kobylak 
293ae0998f1SAdriana Kobylak         try
294ae0998f1SAdriana Kobylak         {
295ae0998f1SAdriana Kobylak             name = iter["element_name"].get<std::string>();
296ae0998f1SAdriana Kobylak             lid = iter["short_lid_name"].get<std::string>();
297ae0998f1SAdriana Kobylak         }
29897a709b0SPatrick Williams         catch (const std::exception& e)
299ae0998f1SAdriana Kobylak         {
300ae0998f1SAdriana Kobylak             // Possibly the element or lid name field was not found
301ae0998f1SAdriana Kobylak             log<level::ERR>("Error reading JSON field",
302ae0998f1SAdriana Kobylak                             entry("FILE=%s", elementsJsonFilePath.c_str()),
303ae0998f1SAdriana Kobylak                             entry("ERROR=%s", e.what()));
304ae0998f1SAdriana Kobylak             continue;
305ae0998f1SAdriana Kobylak         }
306ae0998f1SAdriana Kobylak 
307ae0998f1SAdriana Kobylak         // The elements with the ipl extension have higher priority. Therefore
308*9cbc06b1SAdriana Kobylak         // Use operator[] to overwrite value if an entry for it already exists,
309*9cbc06b1SAdriana Kobylak         // and create a second entry with key name element_RT to specify it as
310*9cbc06b1SAdriana Kobylak         // a runtime element.
311*9cbc06b1SAdriana Kobylak         // Ex: if the JSON contains an entry A.P10 with lid name X, it'll create
312*9cbc06b1SAdriana Kobylak         // and try A=X. If the JSON also contained an entry A.P10.iplTime with
313*9cbc06b1SAdriana Kobylak         // lid name Y, the A entry would be overwritten to be A=Y and a second
314*9cbc06b1SAdriana Kobylak         // entry A_RT=X would be created.
315ae0998f1SAdriana Kobylak         constexpr auto iplExtension = ".iplTime";
316*9cbc06b1SAdriana Kobylak         constexpr auto runtimeSuffix = "_RT";
317ae0998f1SAdriana Kobylak         std::filesystem::path path(name);
318ae0998f1SAdriana Kobylak         if (path.extension() == iplExtension)
319ae0998f1SAdriana Kobylak         {
320ae0998f1SAdriana Kobylak             // Some elements have an additional extension, ex: .P10.iplTime
321ae0998f1SAdriana Kobylak             // Strip off the ipl extension with stem(), then check if there is
322ae0998f1SAdriana Kobylak             // an additional extension with extension().
323ae0998f1SAdriana Kobylak             if (!path.stem().extension().empty())
324ae0998f1SAdriana Kobylak             {
325ae0998f1SAdriana Kobylak                 // Check if the extension matches the extensions for this system
326ae0998f1SAdriana Kobylak                 if (std::find(extensions.begin(), extensions.end(),
327ae0998f1SAdriana Kobylak                               path.stem().extension()) == extensions.end())
328ae0998f1SAdriana Kobylak                 {
329ae0998f1SAdriana Kobylak                     continue;
330ae0998f1SAdriana Kobylak                 }
331ae0998f1SAdriana Kobylak             }
332ae0998f1SAdriana Kobylak             // Get the element name without extensions by calling stem() twice
333ae0998f1SAdriana Kobylak             // since stem() returns the base name if no periods are found.
334ae0998f1SAdriana Kobylak             // Therefore both "element.P10" and "element.P10.iplTime" would
335ae0998f1SAdriana Kobylak             // become "element".
336*9cbc06b1SAdriana Kobylak             auto keyName = path.stem().stem();
337*9cbc06b1SAdriana Kobylak             auto attrIt = attr.find(keyName);
338*9cbc06b1SAdriana Kobylak             if (attrIt != attr.end())
339*9cbc06b1SAdriana Kobylak             {
340*9cbc06b1SAdriana Kobylak                 // Copy the existing entry to a runtime entry
341*9cbc06b1SAdriana Kobylak                 auto runtimeKeyName = keyName.string() + runtimeSuffix;
342*9cbc06b1SAdriana Kobylak                 attr.insert({runtimeKeyName, attrIt->second});
343*9cbc06b1SAdriana Kobylak             }
344*9cbc06b1SAdriana Kobylak             // Overwrite the exsiting element with the ipl entry
345*9cbc06b1SAdriana Kobylak             attr[keyName] = lid;
346ae0998f1SAdriana Kobylak             continue;
347ae0998f1SAdriana Kobylak         }
348ae0998f1SAdriana Kobylak 
349ae0998f1SAdriana Kobylak         // Process all other extensions. The extension should match the list of
350ae0998f1SAdriana Kobylak         // supported extensions for this system. Use .insert() to only add
351ae0998f1SAdriana Kobylak         // entries that do not exist, so to not overwrite the values that may
352ae0998f1SAdriana Kobylak         // had been added that had the ipl extension.
353ae0998f1SAdriana Kobylak         if (std::find(extensions.begin(), extensions.end(), path.extension()) !=
354ae0998f1SAdriana Kobylak             extensions.end())
355ae0998f1SAdriana Kobylak         {
356*9cbc06b1SAdriana Kobylak             auto keyName = path.stem();
357*9cbc06b1SAdriana Kobylak             auto attrIt = attr.find(keyName);
358*9cbc06b1SAdriana Kobylak             if (attrIt != attr.end())
359*9cbc06b1SAdriana Kobylak             {
360*9cbc06b1SAdriana Kobylak                 // The existing entry is an ipl entry, therefore create this
361*9cbc06b1SAdriana Kobylak                 // entry as a runtime one.
362*9cbc06b1SAdriana Kobylak                 auto runtimeKeyName = keyName.string() + runtimeSuffix;
363*9cbc06b1SAdriana Kobylak                 attr.insert({runtimeKeyName, lid});
364*9cbc06b1SAdriana Kobylak             }
365*9cbc06b1SAdriana Kobylak             else
366*9cbc06b1SAdriana Kobylak             {
367ae0998f1SAdriana Kobylak                 attr.insert({path.stem(), lid});
368ae0998f1SAdriana Kobylak             }
369ae0998f1SAdriana Kobylak         }
370*9cbc06b1SAdriana Kobylak     }
371ae0998f1SAdriana Kobylak     for (const auto& a : attr)
372ae0998f1SAdriana Kobylak     {
373ae0998f1SAdriana Kobylak         // Build the bios attribute string with format:
374ae0998f1SAdriana Kobylak         // "element1=lid1,element2=lid2,elementN=lidN,"
375ae0998f1SAdriana Kobylak         biosAttrStr += a.first + "=" + a.second + ",";
3765dc5d6ccSAdriana Kobylak 
3775dc5d6ccSAdriana Kobylak         // Create symlinks from the hostfw elements to their corresponding
3785dc5d6ccSAdriana Kobylak         // lid files if they don't exist
3795dc5d6ccSAdriana Kobylak         auto elementFilePath =
3805dc5d6ccSAdriana Kobylak             std::filesystem::path("/media/hostfw/running") / a.first;
3815dc5d6ccSAdriana Kobylak         if (!std::filesystem::exists(elementFilePath))
3825dc5d6ccSAdriana Kobylak         {
3835dc5d6ccSAdriana Kobylak             std::error_code ec;
3845dc5d6ccSAdriana Kobylak             auto lidName = a.second + ".lid";
3855dc5d6ccSAdriana Kobylak             std::filesystem::create_symlink(lidName, elementFilePath, ec);
3865dc5d6ccSAdriana Kobylak             if (ec)
3875dc5d6ccSAdriana Kobylak             {
3885dc5d6ccSAdriana Kobylak                 log<level::ERR>("Error creating symlink",
3895dc5d6ccSAdriana Kobylak                                 entry("TARGET=%s", lidName.c_str()),
3905dc5d6ccSAdriana Kobylak                                 entry("LINK=%s", elementFilePath.c_str()));
3915dc5d6ccSAdriana Kobylak             }
3925dc5d6ccSAdriana Kobylak         }
393ae0998f1SAdriana Kobylak     }
394ae0998f1SAdriana Kobylak 
395a38f6e65SGeorge Liu     // Delete the last comma of the bios attribute string
396a38f6e65SGeorge Liu     if (biosAttrStr.back() == ',')
397a38f6e65SGeorge Liu     {
398a38f6e65SGeorge Liu         return biosAttrStr.substr(0, biosAttrStr.length() - 1);
399a38f6e65SGeorge Liu     }
400a38f6e65SGeorge Liu 
401ae0998f1SAdriana Kobylak     return biosAttrStr;
402ae0998f1SAdriana Kobylak }
403ae0998f1SAdriana Kobylak 
404ae0998f1SAdriana Kobylak /**
405ae0998f1SAdriana Kobylak  * @brief Set the bios attribute table with details of the host firmware data
406ae0998f1SAdriana Kobylak  * for this system.
407ae0998f1SAdriana Kobylak  *
408ae0998f1SAdriana Kobylak  * @param[in] elementsJsonFilePath - The path to the host firmware json file.
409ae0998f1SAdriana Kobylak  * @param[in] extentions - The extensions of the firmware blob files.
410ae0998f1SAdriana Kobylak  */
411ae0998f1SAdriana Kobylak void setBiosAttr(const std::filesystem::path& elementsJsonFilePath,
412ae0998f1SAdriana Kobylak                  const std::vector<std::string>& extensions)
413ae0998f1SAdriana Kobylak {
414ae0998f1SAdriana Kobylak     auto biosAttrStr = getBiosAttrStr(elementsJsonFilePath, extensions);
415ae0998f1SAdriana Kobylak 
41656f538caSAdriana Kobylak     constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager";
41756f538caSAdriana Kobylak     constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager";
41856f538caSAdriana Kobylak     constexpr auto dbusAttrName = "hb_lid_ids";
41956f538caSAdriana Kobylak     constexpr auto dbusAttrType =
42056f538caSAdriana Kobylak         "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
42156f538caSAdriana Kobylak 
42256f538caSAdriana Kobylak     using PendingAttributesType = std::vector<std::pair<
42356f538caSAdriana Kobylak         std::string, std::tuple<std::string, std::variant<std::string>>>>;
42456f538caSAdriana Kobylak     PendingAttributesType pendingAttributes;
42556f538caSAdriana Kobylak     pendingAttributes.emplace_back(std::make_pair(
42656f538caSAdriana Kobylak         dbusAttrName, std::make_tuple(dbusAttrType, biosAttrStr)));
42756f538caSAdriana Kobylak 
42856f538caSAdriana Kobylak     auto bus = sdbusplus::bus::new_default();
42956f538caSAdriana Kobylak     auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
43056f538caSAdriana Kobylak                                       MAPPER_INTERFACE, "GetObject");
43156f538caSAdriana Kobylak     method.append(biosConfigPath, std::vector<std::string>({biosConfigIntf}));
43256f538caSAdriana Kobylak     std::vector<std::pair<std::string, std::vector<std::string>>> response;
43356f538caSAdriana Kobylak     try
43456f538caSAdriana Kobylak     {
43556f538caSAdriana Kobylak         auto reply = bus.call(method);
43656f538caSAdriana Kobylak         reply.read(response);
43756f538caSAdriana Kobylak         if (response.empty())
43856f538caSAdriana Kobylak         {
43956f538caSAdriana Kobylak             log<level::ERR>("Error reading mapper response",
44056f538caSAdriana Kobylak                             entry("PATH=%s", biosConfigPath),
44156f538caSAdriana Kobylak                             entry("INTERFACE=%s", biosConfigIntf));
44256f538caSAdriana Kobylak             return;
44356f538caSAdriana Kobylak         }
44456f538caSAdriana Kobylak         auto method = bus.new_method_call((response.begin()->first).c_str(),
44556f538caSAdriana Kobylak                                           biosConfigPath,
44656f538caSAdriana Kobylak                                           SYSTEMD_PROPERTY_INTERFACE, "Set");
44756f538caSAdriana Kobylak         method.append(biosConfigIntf, "PendingAttributes",
44856f538caSAdriana Kobylak                       std::variant<PendingAttributesType>(pendingAttributes));
44956f538caSAdriana Kobylak         bus.call(method);
45056f538caSAdriana Kobylak     }
4517b5685d1SPatrick Williams     catch (const sdbusplus::exception::exception& e)
45256f538caSAdriana Kobylak     {
45356f538caSAdriana Kobylak         log<level::ERR>("Error setting the bios attribute",
45456f538caSAdriana Kobylak                         entry("ERROR=%s", e.what()),
45556f538caSAdriana Kobylak                         entry("ATTRIBUTE=%s", dbusAttrName));
45656f538caSAdriana Kobylak         return;
45756f538caSAdriana Kobylak     }
45856f538caSAdriana Kobylak }
45953a27395SAdriana Kobylak 
46053a27395SAdriana Kobylak /**
461099543e4SBrad Bishop  * @brief Make callbacks on
462099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
463099543e4SBrad Bishop  *
464099543e4SBrad Bishop  * Look for an instance of
465099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
466099543e4SBrad Bishop  * argument and if found, issue the provided callback.
467099543e4SBrad Bishop  *
468099543e4SBrad Bishop  * @param[in] interfacesAndProperties the interfaces in which to look for an
469099543e4SBrad Bishop  * instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem
470099543e4SBrad Bishop  * @param[in] callback the user callback to make if
471099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
472099543e4SBrad Bishop  * interfacesAndProperties
473099543e4SBrad Bishop  * @return true if interfacesAndProperties contained an instance of
474099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
475099543e4SBrad Bishop  */
476099543e4SBrad Bishop bool maybeCall(const std::map<std::string,
477099543e4SBrad Bishop                               std::map<std::string,
478099543e4SBrad Bishop                                        std::variant<std::vector<std::string>>>>&
479099543e4SBrad Bishop                    interfacesAndProperties,
480099543e4SBrad Bishop                const MaybeCallCallbackType& callback)
481099543e4SBrad Bishop {
482099543e4SBrad Bishop     using namespace std::string_literals;
483099543e4SBrad Bishop 
484099543e4SBrad Bishop     static const auto interfaceName =
485099543e4SBrad Bishop         "xyz.openbmc_project.Configuration.IBMCompatibleSystem"s;
486099543e4SBrad Bishop     auto interfaceIterator = interfacesAndProperties.find(interfaceName);
487099543e4SBrad Bishop     if (interfaceIterator == interfacesAndProperties.cend())
488099543e4SBrad Bishop     {
489099543e4SBrad Bishop         // IBMCompatibleSystem interface not found, so instruct the caller to
490099543e4SBrad Bishop         // keep waiting or try again later.
491099543e4SBrad Bishop         return false;
492099543e4SBrad Bishop     }
493099543e4SBrad Bishop     auto propertyIterator = interfaceIterator->second.find("Names"s);
494099543e4SBrad Bishop     if (propertyIterator == interfaceIterator->second.cend())
495099543e4SBrad Bishop     {
496099543e4SBrad Bishop         // The interface exists but the property doesn't.  This is a bug in the
497099543e4SBrad Bishop         // IBMCompatibleSystem implementation.  The caller should not try
498099543e4SBrad Bishop         // again.
499099543e4SBrad Bishop         std::cerr << "Names property not implemented on " << interfaceName
500099543e4SBrad Bishop                   << "\n";
501099543e4SBrad Bishop         return true;
502099543e4SBrad Bishop     }
503099543e4SBrad Bishop 
504099543e4SBrad Bishop     const auto& ibmCompatibleSystem =
505099543e4SBrad Bishop         std::get<std::vector<std::string>>(propertyIterator->second);
506099543e4SBrad Bishop     if (callback)
507099543e4SBrad Bishop     {
508099543e4SBrad Bishop         callback(ibmCompatibleSystem);
509099543e4SBrad Bishop     }
510099543e4SBrad Bishop 
511099543e4SBrad Bishop     // IBMCompatibleSystem found and callback issued.
512099543e4SBrad Bishop     return true;
513099543e4SBrad Bishop }
514099543e4SBrad Bishop 
515099543e4SBrad Bishop /**
516099543e4SBrad Bishop  * @brief Make callbacks on
517099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
518099543e4SBrad Bishop  *
519099543e4SBrad Bishop  * Look for an instance of
520099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
521099543e4SBrad Bishop  * argument and if found, issue the provided callback.
522099543e4SBrad Bishop  *
523099543e4SBrad Bishop  * @param[in] message the DBus message in which to look for an instance of
524099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem
525099543e4SBrad Bishop  * @param[in] callback the user callback to make if
526099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
527099543e4SBrad Bishop  * message
528099543e4SBrad Bishop  * @return true if message contained an instance of
529099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
530099543e4SBrad Bishop  */
531099543e4SBrad Bishop bool maybeCallMessage(sdbusplus::message::message& message,
532099543e4SBrad Bishop                       const MaybeCallCallbackType& callback)
533099543e4SBrad Bishop {
534099543e4SBrad Bishop     std::map<std::string,
535099543e4SBrad Bishop              std::map<std::string, std::variant<std::vector<std::string>>>>
536099543e4SBrad Bishop         interfacesAndProperties;
537099543e4SBrad Bishop     sdbusplus::message::object_path _;
538099543e4SBrad Bishop     message.read(_, interfacesAndProperties);
539099543e4SBrad Bishop     return maybeCall(interfacesAndProperties, callback);
540099543e4SBrad Bishop }
541099543e4SBrad Bishop 
542099543e4SBrad Bishop /**
543099543e4SBrad Bishop  * @brief Determine system support for host firmware well-known names.
544099543e4SBrad Bishop  *
545099543e4SBrad Bishop  * Using the provided extensionMap and
546099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if
547099543e4SBrad Bishop  * well-known names for host firmare blob files are necessary and if so, create
548099543e4SBrad Bishop  * them.
549099543e4SBrad Bishop  *
550099543e4SBrad Bishop  * @param[in] extensionMap a map of
551099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
552099543e4SBrad Bishop  * file extensions.
553099543e4SBrad Bishop  * @param[in] hostFirmwareDirectory The directory in which findLinks should
554099543e4SBrad Bishop  * look for host firmware blob files that need well-known names.
555099543e4SBrad Bishop  * @param[in] ibmCompatibleSystem The names property of an instance of
556099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem
557099543e4SBrad Bishop  * @param[in] errorCallback A callback made in the event of filesystem errors.
558099543e4SBrad Bishop  */
559099543e4SBrad Bishop void maybeMakeLinks(
560099543e4SBrad Bishop     const std::map<std::string, std::vector<std::string>>& extensionMap,
561099543e4SBrad Bishop     const std::filesystem::path& hostFirmwareDirectory,
562099543e4SBrad Bishop     const std::vector<std::string>& ibmCompatibleSystem,
563099543e4SBrad Bishop     const ErrorCallbackType& errorCallback)
564099543e4SBrad Bishop {
565099543e4SBrad Bishop     std::vector<std::string> extensions;
566099543e4SBrad Bishop     if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
567099543e4SBrad Bishop                                             extensions))
568099543e4SBrad Bishop     {
569099543e4SBrad Bishop         findLinks(hostFirmwareDirectory, extensions, errorCallback, writeLink);
570099543e4SBrad Bishop     }
571099543e4SBrad Bishop }
572099543e4SBrad Bishop 
573099543e4SBrad Bishop /**
57453a27395SAdriana Kobylak  * @brief Determine system support for updating the bios attribute table.
57553a27395SAdriana Kobylak  *
57653a27395SAdriana Kobylak  * Using the provided extensionMap and
57753a27395SAdriana Kobylak  * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if the bios
57853a27395SAdriana Kobylak  * attribute table needs to be updated.
57953a27395SAdriana Kobylak  *
58053a27395SAdriana Kobylak  * @param[in] extensionMap a map of
58153a27395SAdriana Kobylak  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
58253a27395SAdriana Kobylak  * file extensions.
583ae0998f1SAdriana Kobylak  * @param[in] elementsJsonFilePath The file path to the json file
58453a27395SAdriana Kobylak  * @param[in] ibmCompatibleSystem The names property of an instance of
58553a27395SAdriana Kobylak  * xyz.openbmc_project.Configuration.IBMCompatibleSystem
58653a27395SAdriana Kobylak  */
58753a27395SAdriana Kobylak void maybeSetBiosAttr(
58853a27395SAdriana Kobylak     const std::map<std::string, std::vector<std::string>>& extensionMap,
589ae0998f1SAdriana Kobylak     const std::filesystem::path& elementsJsonFilePath,
59053a27395SAdriana Kobylak     const std::vector<std::string>& ibmCompatibleSystem)
59153a27395SAdriana Kobylak {
59253a27395SAdriana Kobylak     std::vector<std::string> extensions;
59353a27395SAdriana Kobylak     if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
59453a27395SAdriana Kobylak                                             extensions))
59553a27395SAdriana Kobylak     {
596ae0998f1SAdriana Kobylak         setBiosAttr(elementsJsonFilePath, extensions);
59753a27395SAdriana Kobylak     }
59853a27395SAdriana Kobylak }
59953a27395SAdriana Kobylak 
60053a27395SAdriana Kobylak /**
601099543e4SBrad Bishop  * @brief process host firmware
602099543e4SBrad Bishop  *
603099543e4SBrad Bishop  * Allocate a callback context and register for DBus.ObjectManager Interfaces
604099543e4SBrad Bishop  * added signals from entity manager.
605099543e4SBrad Bishop  *
606099543e4SBrad Bishop  * Check the current entity manager object tree for a
607099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem instance (entity
608099543e4SBrad Bishop  * manager will be dbus activated if it is not running).  If one is found,
609099543e4SBrad Bishop  * determine if symlinks need to be created and create them.  Instruct the
610099543e4SBrad Bishop  * program event loop to exit.
611099543e4SBrad Bishop  *
612099543e4SBrad Bishop  * If no instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
613099543e4SBrad Bishop  * found return the callback context to main, where the program will sleep
614099543e4SBrad Bishop  * until the callback is invoked one or more times and instructs the program
615099543e4SBrad Bishop  * event loop to exit when
616099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem is added.
617099543e4SBrad Bishop  *
618099543e4SBrad Bishop  * @param[in] bus a DBus client connection
619099543e4SBrad Bishop  * @param[in] extensionMap a map of
620099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
621099543e4SBrad Bishop  * file extensions.
622099543e4SBrad Bishop  * @param[in] hostFirmwareDirectory The directory in which processHostFirmware
623099543e4SBrad Bishop  * should look for blob files.
624099543e4SBrad Bishop  * @param[in] errorCallback A callback made in the event of filesystem errors.
625099543e4SBrad Bishop  * @param[in] loop a program event loop
626099543e4SBrad Bishop  * @return nullptr if an instance of
627099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found, otherwise a
628099543e4SBrad Bishop  * pointer to an sdbusplus match object.
629099543e4SBrad Bishop  */
630099543e4SBrad Bishop std::shared_ptr<void> processHostFirmware(
631099543e4SBrad Bishop     sdbusplus::bus::bus& bus,
632099543e4SBrad Bishop     std::map<std::string, std::vector<std::string>> extensionMap,
633099543e4SBrad Bishop     std::filesystem::path hostFirmwareDirectory,
634099543e4SBrad Bishop     ErrorCallbackType errorCallback, sdeventplus::Event& loop)
635099543e4SBrad Bishop {
636099543e4SBrad Bishop     // ownership of extensionMap, hostFirmwareDirectory and errorCallback can't
637099543e4SBrad Bishop     // be transfered to the match callback because they are needed in the non
638099543e4SBrad Bishop     // async part of this function below, so they need to be moved to the heap.
639099543e4SBrad Bishop     auto pExtensionMap =
640099543e4SBrad Bishop         std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
641099543e4SBrad Bishop     auto pHostFirmwareDirectory =
642099543e4SBrad Bishop         std::make_shared<decltype(hostFirmwareDirectory)>(
643099543e4SBrad Bishop             std::move(hostFirmwareDirectory));
644099543e4SBrad Bishop     auto pErrorCallback =
645099543e4SBrad Bishop         std::make_shared<decltype(errorCallback)>(std::move(errorCallback));
646099543e4SBrad Bishop 
647099543e4SBrad Bishop     // register for a callback in case the IBMCompatibleSystem interface has
648099543e4SBrad Bishop     // not yet been published by entity manager.
649099543e4SBrad Bishop     auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match::match>(
650099543e4SBrad Bishop         bus,
651099543e4SBrad Bishop         sdbusplus::bus::match::rules::interfacesAdded() +
652099543e4SBrad Bishop             sdbusplus::bus::match::rules::sender(
653099543e4SBrad Bishop                 "xyz.openbmc_project.EntityManager"),
654099543e4SBrad Bishop         [pExtensionMap, pHostFirmwareDirectory, pErrorCallback,
655099543e4SBrad Bishop          &loop](auto& message) {
656099543e4SBrad Bishop             // bind the extension map, host firmware directory, and error
657099543e4SBrad Bishop             // callback to the maybeMakeLinks function.
658099543e4SBrad Bishop             auto maybeMakeLinksWithArgsBound =
659099543e4SBrad Bishop                 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
660099543e4SBrad Bishop                           std::cref(*pHostFirmwareDirectory),
661099543e4SBrad Bishop                           std::placeholders::_1, std::cref(*pErrorCallback));
662099543e4SBrad Bishop 
663099543e4SBrad Bishop             // if the InterfacesAdded message contains an an instance of
664099543e4SBrad Bishop             // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to
665099543e4SBrad Bishop             // see if links are necessary on this system and if so, create
666099543e4SBrad Bishop             // them.
667099543e4SBrad Bishop             if (maybeCallMessage(message, maybeMakeLinksWithArgsBound))
668099543e4SBrad Bishop             {
669099543e4SBrad Bishop                 // The IBMCompatibleSystem interface was found and the links
670099543e4SBrad Bishop                 // were created if applicable.  Instruct the event loop /
671099543e4SBrad Bishop                 // subcommand to exit.
672099543e4SBrad Bishop                 loop.exit(0);
673099543e4SBrad Bishop             }
674099543e4SBrad Bishop         });
675099543e4SBrad Bishop 
676099543e4SBrad Bishop     // now that we'll get a callback in the event of an InterfacesAdded signal
677099543e4SBrad Bishop     // (potentially containing
678099543e4SBrad Bishop     // xyz.openbmc_project.Configuration.IBMCompatibleSystem), activate entity
679099543e4SBrad Bishop     // manager if it isn't running and enumerate its objects
680099543e4SBrad Bishop     auto getManagedObjects = bus.new_method_call(
681099543e4SBrad Bishop         "xyz.openbmc_project.EntityManager", "/",
682099543e4SBrad Bishop         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
683099543e4SBrad Bishop     std::map<std::string,
684099543e4SBrad Bishop              std::map<std::string, std::variant<std::vector<std::string>>>>
685099543e4SBrad Bishop         interfacesAndProperties;
686099543e4SBrad Bishop     std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)>
687099543e4SBrad Bishop         objects;
688c79fa915SAdriana Kobylak     try
689c79fa915SAdriana Kobylak     {
690c79fa915SAdriana Kobylak         auto reply = bus.call(getManagedObjects);
691099543e4SBrad Bishop         reply.read(objects);
692c79fa915SAdriana Kobylak     }
6937b5685d1SPatrick Williams     catch (const sdbusplus::exception::exception& e)
694c79fa915SAdriana Kobylak     {
695c79fa915SAdriana Kobylak         // Error querying the EntityManager interface. Return the match to have
696c79fa915SAdriana Kobylak         // the callback run if/when the interface appears in D-Bus.
697c79fa915SAdriana Kobylak         return interfacesAddedMatch;
698c79fa915SAdriana Kobylak     }
699099543e4SBrad Bishop 
700099543e4SBrad Bishop     // bind the extension map, host firmware directory, and error callback to
701099543e4SBrad Bishop     // the maybeMakeLinks function.
702099543e4SBrad Bishop     auto maybeMakeLinksWithArgsBound =
703099543e4SBrad Bishop         std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
704099543e4SBrad Bishop                   std::cref(*pHostFirmwareDirectory), std::placeholders::_1,
705099543e4SBrad Bishop                   std::cref(*pErrorCallback));
706099543e4SBrad Bishop 
707099543e4SBrad Bishop     for (const auto& pair : objects)
708099543e4SBrad Bishop     {
709099543e4SBrad Bishop         std::tie(std::ignore, interfacesAndProperties) = pair;
710099543e4SBrad Bishop         // if interfacesAndProperties contains an an instance of
711099543e4SBrad Bishop         // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to see
712099543e4SBrad Bishop         // if links are necessary on this system and if so, create them
713099543e4SBrad Bishop         if (maybeCall(interfacesAndProperties, maybeMakeLinksWithArgsBound))
714099543e4SBrad Bishop         {
715099543e4SBrad Bishop             // The IBMCompatibleSystem interface is already on the bus and the
716099543e4SBrad Bishop             // links were created if applicable.  Instruct the event loop to
717099543e4SBrad Bishop             // exit.
718099543e4SBrad Bishop             loop.exit(0);
719099543e4SBrad Bishop             // The match object isn't needed anymore, so destroy it on return.
720099543e4SBrad Bishop             return nullptr;
721099543e4SBrad Bishop         }
722099543e4SBrad Bishop     }
723099543e4SBrad Bishop 
724099543e4SBrad Bishop     // The IBMCompatibleSystem interface has not yet been published.  Move
725099543e4SBrad Bishop     // ownership of the match callback to the caller.
726099543e4SBrad Bishop     return interfacesAddedMatch;
727099543e4SBrad Bishop }
72853a27395SAdriana Kobylak 
72953a27395SAdriana Kobylak /**
73053a27395SAdriana Kobylak  * @brief Update the Bios Attribute Table
73153a27395SAdriana Kobylak  *
73253a27395SAdriana Kobylak  * If an instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
73353a27395SAdriana Kobylak  * found, update the Bios Attribute Table with the appropriate host firmware
73453a27395SAdriana Kobylak  * data.
73553a27395SAdriana Kobylak  *
73653a27395SAdriana Kobylak  * @param[in] bus - D-Bus client connection.
73753a27395SAdriana Kobylak  * @param[in] extensionMap - Map of IBMCompatibleSystem names and host firmware
73853a27395SAdriana Kobylak  *                           file extensions.
739ae0998f1SAdriana Kobylak  * @param[in] elementsJsonFilePath - The Path to the json file
74053a27395SAdriana Kobylak  * @param[in] loop - Program event loop.
74153a27395SAdriana Kobylak  * @return nullptr
74253a27395SAdriana Kobylak  */
743ebf67bf7SAdriana Kobylak std::vector<std::shared_ptr<void>> updateBiosAttrTable(
74453a27395SAdriana Kobylak     sdbusplus::bus::bus& bus,
74553a27395SAdriana Kobylak     std::map<std::string, std::vector<std::string>> extensionMap,
746ae0998f1SAdriana Kobylak     std::filesystem::path elementsJsonFilePath, sdeventplus::Event& loop)
74753a27395SAdriana Kobylak {
748d0379ea5SAdriana Kobylak     constexpr auto pldmPath = "/xyz/openbmc_project/pldm";
749ebf67bf7SAdriana Kobylak     constexpr auto entityManagerServiceName =
750ebf67bf7SAdriana Kobylak         "xyz.openbmc_project.EntityManager";
751d0379ea5SAdriana Kobylak 
75253a27395SAdriana Kobylak     auto pExtensionMap =
75353a27395SAdriana Kobylak         std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
754ae0998f1SAdriana Kobylak     auto pElementsJsonFilePath =
755ae0998f1SAdriana Kobylak         std::make_shared<decltype(elementsJsonFilePath)>(
756ae0998f1SAdriana Kobylak             std::move(elementsJsonFilePath));
75753a27395SAdriana Kobylak 
758d0379ea5SAdriana Kobylak     // Entity Manager is needed to get the list of supported extensions. Add a
759d0379ea5SAdriana Kobylak     // match to monitor interfaces added in case it's not running yet.
760d0379ea5SAdriana Kobylak     auto maybeSetAttrWithArgsBound =
761d0379ea5SAdriana Kobylak         std::bind(maybeSetBiosAttr, std::cref(*pExtensionMap),
762d0379ea5SAdriana Kobylak                   std::cref(*pElementsJsonFilePath), std::placeholders::_1);
763d0379ea5SAdriana Kobylak 
764ebf67bf7SAdriana Kobylak     std::vector<std::shared_ptr<void>> matches;
765ebf67bf7SAdriana Kobylak     matches.emplace_back(std::make_shared<sdbusplus::bus::match::match>(
766d0379ea5SAdriana Kobylak         bus,
767d0379ea5SAdriana Kobylak         sdbusplus::bus::match::rules::interfacesAdded() +
768d0379ea5SAdriana Kobylak             sdbusplus::bus::match::rules::sender(
769d0379ea5SAdriana Kobylak                 "xyz.openbmc_project.EntityManager"),
770d0379ea5SAdriana Kobylak         [pldmPath, pExtensionMap, pElementsJsonFilePath,
771d0379ea5SAdriana Kobylak          maybeSetAttrWithArgsBound, &loop](auto& message) {
772d0379ea5SAdriana Kobylak             auto bus = sdbusplus::bus::new_default();
773fd4a6088SAdriana Kobylak             auto pldmObject = getObject(bus, pldmPath);
774fd4a6088SAdriana Kobylak             if (pldmObject.empty())
775fd4a6088SAdriana Kobylak             {
776d0379ea5SAdriana Kobylak                 return;
777d0379ea5SAdriana Kobylak             }
778d0379ea5SAdriana Kobylak             if (maybeCallMessage(message, maybeSetAttrWithArgsBound))
779d0379ea5SAdriana Kobylak             {
780fd4a6088SAdriana Kobylak                 loop.exit(0);
781d0379ea5SAdriana Kobylak             }
782ebf67bf7SAdriana Kobylak         }));
783ebf67bf7SAdriana Kobylak     matches.emplace_back(std::make_shared<sdbusplus::bus::match::match>(
784ebf67bf7SAdriana Kobylak         bus,
785ebf67bf7SAdriana Kobylak         sdbusplus::bus::match::rules::nameOwnerChanged() +
786ebf67bf7SAdriana Kobylak             sdbusplus::bus::match::rules::arg0namespace(
787ebf67bf7SAdriana Kobylak                 "xyz.openbmc_project.PLDM"),
788ebf67bf7SAdriana Kobylak         [pExtensionMap, pElementsJsonFilePath, maybeSetAttrWithArgsBound,
789ebf67bf7SAdriana Kobylak          &loop](auto& message) {
790ebf67bf7SAdriana Kobylak             std::string name;
791ebf67bf7SAdriana Kobylak             std::string oldOwner;
792ebf67bf7SAdriana Kobylak             std::string newOwner;
793ebf67bf7SAdriana Kobylak             message.read(name, oldOwner, newOwner);
794ebf67bf7SAdriana Kobylak 
795ebf67bf7SAdriana Kobylak             if (newOwner.empty())
796ebf67bf7SAdriana Kobylak             {
797ebf67bf7SAdriana Kobylak                 return;
798ebf67bf7SAdriana Kobylak             }
799ebf67bf7SAdriana Kobylak 
800ebf67bf7SAdriana Kobylak             auto bus = sdbusplus::bus::new_default();
801ebf67bf7SAdriana Kobylak             InterfacesPropertiesMap interfacesAndProperties;
802ebf67bf7SAdriana Kobylak             auto objects = getManagedObjects(bus, entityManagerServiceName);
803ebf67bf7SAdriana Kobylak             for (const auto& pair : objects)
804ebf67bf7SAdriana Kobylak             {
805ebf67bf7SAdriana Kobylak                 std::tie(std::ignore, interfacesAndProperties) = pair;
806ebf67bf7SAdriana Kobylak                 if (maybeCall(interfacesAndProperties,
807ebf67bf7SAdriana Kobylak                               maybeSetAttrWithArgsBound))
808ebf67bf7SAdriana Kobylak                 {
809ebf67bf7SAdriana Kobylak                     loop.exit(0);
810ebf67bf7SAdriana Kobylak                 }
811ebf67bf7SAdriana Kobylak             }
812ebf67bf7SAdriana Kobylak         }));
813d0379ea5SAdriana Kobylak 
814d0379ea5SAdriana Kobylak     // The BIOS attribute table can only be updated if PLDM is running because
815d0379ea5SAdriana Kobylak     // PLDM is the one that exposes this property. Return if it's not running.
816d0379ea5SAdriana Kobylak     auto pldmObject = getObject(bus, pldmPath);
817d0379ea5SAdriana Kobylak     if (pldmObject.empty())
818d0379ea5SAdriana Kobylak     {
819ebf67bf7SAdriana Kobylak         return matches;
820fd4a6088SAdriana Kobylak     }
821fd4a6088SAdriana Kobylak 
822ebf67bf7SAdriana Kobylak     InterfacesPropertiesMap interfacesAndProperties;
823ebf67bf7SAdriana Kobylak     auto objects = getManagedObjects(bus, entityManagerServiceName);
82453a27395SAdriana Kobylak     for (const auto& pair : objects)
82553a27395SAdriana Kobylak     {
82653a27395SAdriana Kobylak         std::tie(std::ignore, interfacesAndProperties) = pair;
82753a27395SAdriana Kobylak         if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound))
82853a27395SAdriana Kobylak         {
829d0379ea5SAdriana Kobylak             loop.exit(0);
830ebf67bf7SAdriana Kobylak             return {};
83153a27395SAdriana Kobylak         }
83253a27395SAdriana Kobylak     }
83353a27395SAdriana Kobylak 
834ebf67bf7SAdriana Kobylak     return matches;
83553a27395SAdriana Kobylak }
83653a27395SAdriana Kobylak 
837099543e4SBrad Bishop } // namespace process_hostfirmware
838099543e4SBrad Bishop } // namespace functions
839