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 
9*ae0998f1SAdriana 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>
18*ae0998f1SAdriana 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;
3356f538caSAdriana Kobylak 
34099543e4SBrad Bishop /**
35099543e4SBrad Bishop  * @brief Issue callbacks safely
36099543e4SBrad Bishop  *
37099543e4SBrad Bishop  * std::function can be empty, so this wrapper method checks for that prior to
38099543e4SBrad Bishop  * calling it to avoid std::bad_function_call
39099543e4SBrad Bishop  *
40099543e4SBrad Bishop  * @tparam Sig the types of the std::function arguments
41099543e4SBrad Bishop  * @tparam Args the deduced argument types
42099543e4SBrad Bishop  * @param[in] callback the callback being wrapped
43099543e4SBrad Bishop  * @param[in] args the callback arguments
44099543e4SBrad Bishop  */
45099543e4SBrad Bishop template <typename... Sig, typename... Args>
46099543e4SBrad Bishop void makeCallback(const std::function<void(Sig...)>& callback, Args&&... args)
47099543e4SBrad Bishop {
48099543e4SBrad Bishop     if (callback)
49099543e4SBrad Bishop     {
50099543e4SBrad Bishop         callback(std::forward<Args>(args)...);
51099543e4SBrad Bishop     }
52099543e4SBrad Bishop }
53099543e4SBrad Bishop 
54099543e4SBrad Bishop /**
55099543e4SBrad Bishop  * @brief Get file extensions for IBMCompatibleSystem
56099543e4SBrad Bishop  *
57099543e4SBrad Bishop  * IBM host firmware can be deployed as blobs (files) in a filesystem.  Host
58099543e4SBrad Bishop  * firmware blobs for different values of
59099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem are packaged with
60099543e4SBrad Bishop  * different filename extensions.  getExtensionsForIbmCompatibleSystem
61099543e4SBrad Bishop  * maintains the mapping from a given value of
62099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to an array of
63099543e4SBrad Bishop  * filename extensions.
64099543e4SBrad Bishop  *
65099543e4SBrad Bishop  * If a mapping is found getExtensionsForIbmCompatibleSystem returns true and
66099543e4SBrad Bishop  * the extensions parameter is reset with the map entry.  If no mapping is
67099543e4SBrad Bishop  * found getExtensionsForIbmCompatibleSystem returns false and extensions is
68099543e4SBrad Bishop  * unmodified.
69099543e4SBrad Bishop  *
70099543e4SBrad Bishop  * @param[in] extensionMap a map of
71099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
72099543e4SBrad Bishop  * file extensions.
73099543e4SBrad Bishop  * @param[in] ibmCompatibleSystem The names property of an instance of
74099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem
75099543e4SBrad Bishop  * @param[out] extentions the host firmware blob file extensions
76099543e4SBrad Bishop  * @return true if an entry was found, otherwise false
77099543e4SBrad Bishop  */
78099543e4SBrad Bishop bool getExtensionsForIbmCompatibleSystem(
79099543e4SBrad Bishop     const std::map<std::string, std::vector<std::string>>& extensionMap,
80099543e4SBrad Bishop     const std::vector<std::string>& ibmCompatibleSystem,
81099543e4SBrad Bishop     std::vector<std::string>& extensions)
82099543e4SBrad Bishop {
83099543e4SBrad Bishop     for (const auto& system : ibmCompatibleSystem)
84099543e4SBrad Bishop     {
85099543e4SBrad Bishop         auto extensionMapIterator = extensionMap.find(system);
86099543e4SBrad Bishop         if (extensionMapIterator != extensionMap.end())
87099543e4SBrad Bishop         {
88099543e4SBrad Bishop             extensions = extensionMapIterator->second;
89099543e4SBrad Bishop             return true;
90099543e4SBrad Bishop         }
91099543e4SBrad Bishop     }
92099543e4SBrad Bishop 
93099543e4SBrad Bishop     return false;
94099543e4SBrad Bishop }
95099543e4SBrad Bishop 
96099543e4SBrad Bishop /**
97099543e4SBrad Bishop  * @brief Write host firmware well-known name
98099543e4SBrad Bishop  *
99099543e4SBrad Bishop  * A wrapper around std::filesystem::create_symlink that avoids EEXIST by
100099543e4SBrad Bishop  * deleting any pre-existing file.
101099543e4SBrad Bishop  *
102099543e4SBrad Bishop  * @param[in] linkTarget The link target argument to
103099543e4SBrad Bishop  * std::filesystem::create_symlink
104099543e4SBrad Bishop  * @param[in] linkPath The link path argument to std::filesystem::create_symlink
105099543e4SBrad Bishop  * @param[in] errorCallback A callback made in the event of filesystem errors.
106099543e4SBrad Bishop  */
107099543e4SBrad Bishop void writeLink(const std::filesystem::path& linkTarget,
108099543e4SBrad Bishop                const std::filesystem::path& linkPath,
109099543e4SBrad Bishop                const ErrorCallbackType& errorCallback)
110099543e4SBrad Bishop {
111099543e4SBrad Bishop     std::error_code ec;
112099543e4SBrad Bishop 
113099543e4SBrad Bishop     // remove files with the same name as the symlink to be created,
114099543e4SBrad Bishop     // otherwise symlink will fail with EEXIST.
115099543e4SBrad Bishop     if (!std::filesystem::remove(linkPath, ec))
116099543e4SBrad Bishop     {
117099543e4SBrad Bishop         if (ec)
118099543e4SBrad Bishop         {
119099543e4SBrad Bishop             makeCallback(errorCallback, linkPath, ec);
120099543e4SBrad Bishop             return;
121099543e4SBrad Bishop         }
122099543e4SBrad Bishop     }
123099543e4SBrad Bishop 
124099543e4SBrad Bishop     std::filesystem::create_symlink(linkTarget, linkPath, ec);
125099543e4SBrad Bishop     if (ec)
126099543e4SBrad Bishop     {
127099543e4SBrad Bishop         makeCallback(errorCallback, linkPath, ec);
128099543e4SBrad Bishop         return;
129099543e4SBrad Bishop     }
130099543e4SBrad Bishop }
131099543e4SBrad Bishop 
132099543e4SBrad Bishop /**
133099543e4SBrad Bishop  * @brief Find host firmware blob files that need well-known names
134099543e4SBrad Bishop  *
135099543e4SBrad Bishop  * The IBM host firmware runtime looks for data and/or additional code while
136099543e4SBrad Bishop  * bootstraping in files with well-known names.  findLinks uses the provided
137099543e4SBrad Bishop  * extensions argument to find host firmware blob files that require a
138099543e4SBrad Bishop  * well-known name.  When a blob is found, issue the provided callback
139099543e4SBrad Bishop  * (typically a function that will write a symlink).
140099543e4SBrad Bishop  *
141099543e4SBrad Bishop  * @param[in] hostFirmwareDirectory The directory in which findLinks should
142099543e4SBrad Bishop  * look for host firmware blob files that need well-known names.
143099543e4SBrad Bishop  * @param[in] extentions The extensions of the firmware blob files denote a
144099543e4SBrad Bishop  * host firmware blob file requires a well-known name.
145099543e4SBrad Bishop  * @param[in] errorCallback A callback made in the event of filesystem errors.
146099543e4SBrad Bishop  * @param[in] linkCallback A callback made when host firmware blob files
147099543e4SBrad Bishop  * needing a well known name are found.
148099543e4SBrad Bishop  */
149099543e4SBrad Bishop void findLinks(const std::filesystem::path& hostFirmwareDirectory,
150099543e4SBrad Bishop                const std::vector<std::string>& extensions,
151099543e4SBrad Bishop                const ErrorCallbackType& errorCallback,
152099543e4SBrad Bishop                const LinkCallbackType& linkCallback)
153099543e4SBrad Bishop {
154099543e4SBrad Bishop     std::error_code ec;
155099543e4SBrad Bishop     std::filesystem::directory_iterator directoryIterator(hostFirmwareDirectory,
156099543e4SBrad Bishop                                                           ec);
157099543e4SBrad Bishop     if (ec)
158099543e4SBrad Bishop     {
159099543e4SBrad Bishop         makeCallback(errorCallback, hostFirmwareDirectory, ec);
160099543e4SBrad Bishop         return;
161099543e4SBrad Bishop     }
162099543e4SBrad Bishop 
163fdc91fa0SAdriana Kobylak     // Create a symlink from HBB to the corresponding LID file if it exists
164fdc91fa0SAdriana Kobylak     static const auto hbbLid = "81e0065a.lid";
165fdc91fa0SAdriana Kobylak     auto hbbLidPath = hostFirmwareDirectory / hbbLid;
166fdc91fa0SAdriana Kobylak     if (std::filesystem::exists(hbbLidPath))
167fdc91fa0SAdriana Kobylak     {
168fdc91fa0SAdriana Kobylak         static const auto hbbName = "HBB";
169fdc91fa0SAdriana Kobylak         auto hbbLinkPath = hostFirmwareDirectory / hbbName;
170fdc91fa0SAdriana Kobylak         makeCallback(linkCallback, hbbLid, hbbLinkPath, errorCallback);
171fdc91fa0SAdriana Kobylak     }
172fdc91fa0SAdriana Kobylak 
173099543e4SBrad Bishop     for (; directoryIterator != std::filesystem::end(directoryIterator);
174099543e4SBrad Bishop          directoryIterator.increment(ec))
175099543e4SBrad Bishop     {
176099543e4SBrad Bishop         const auto& file = directoryIterator->path();
177099543e4SBrad Bishop         if (ec)
178099543e4SBrad Bishop         {
179099543e4SBrad Bishop             makeCallback(errorCallback, file, ec);
180099543e4SBrad Bishop             // quit here if the increment call failed otherwise the loop may
181099543e4SBrad Bishop             // never finish
182099543e4SBrad Bishop             break;
183099543e4SBrad Bishop         }
184099543e4SBrad Bishop 
185099543e4SBrad Bishop         if (std::find(extensions.begin(), extensions.end(), file.extension()) ==
186099543e4SBrad Bishop             extensions.end())
187099543e4SBrad Bishop         {
188099543e4SBrad Bishop             // this file doesn't have an extension or doesn't match any of the
189099543e4SBrad Bishop             // provided extensions.
190099543e4SBrad Bishop             continue;
191099543e4SBrad Bishop         }
192099543e4SBrad Bishop 
193099543e4SBrad Bishop         auto linkPath(file.parent_path().append(
194099543e4SBrad Bishop             static_cast<const std::string&>(file.stem())));
195099543e4SBrad Bishop 
196099543e4SBrad Bishop         makeCallback(linkCallback, file.filename(), linkPath, errorCallback);
197099543e4SBrad Bishop     }
198099543e4SBrad Bishop }
199099543e4SBrad Bishop 
200099543e4SBrad Bishop /**
201*ae0998f1SAdriana Kobylak  * @brief Parse the elements json file and construct a string with the data to
202*ae0998f1SAdriana Kobylak  *        be used to update the bios attribute table.
203*ae0998f1SAdriana Kobylak  *
204*ae0998f1SAdriana Kobylak  * @param[in] elementsJsonFilePath - The path to the host firmware json file.
205*ae0998f1SAdriana Kobylak  * @param[in] extensions - The extensions of the firmware blob files.
20653a27395SAdriana Kobylak  */
207*ae0998f1SAdriana Kobylak std::string getBiosAttrStr(const std::filesystem::path& elementsJsonFilePath,
208*ae0998f1SAdriana Kobylak                            const std::vector<std::string>& extensions)
20956f538caSAdriana Kobylak {
21056f538caSAdriana Kobylak     std::string biosAttrStr{};
21156f538caSAdriana Kobylak 
212*ae0998f1SAdriana Kobylak     std::ifstream jsonFile(elementsJsonFilePath.c_str());
213*ae0998f1SAdriana Kobylak     if (!jsonFile)
214*ae0998f1SAdriana Kobylak     {
215*ae0998f1SAdriana Kobylak         return {};
216*ae0998f1SAdriana Kobylak     }
217*ae0998f1SAdriana Kobylak 
218*ae0998f1SAdriana Kobylak     std::map<std::string, std::string> attr;
219*ae0998f1SAdriana Kobylak     auto data = nlohmann::json::parse(jsonFile, nullptr, false);
220*ae0998f1SAdriana Kobylak     if (data.is_discarded())
221*ae0998f1SAdriana Kobylak     {
222*ae0998f1SAdriana Kobylak         log<level::ERR>("Error parsing JSON file",
223*ae0998f1SAdriana Kobylak                         entry("FILE=%s", elementsJsonFilePath.c_str()));
224*ae0998f1SAdriana Kobylak         return {};
225*ae0998f1SAdriana Kobylak     }
226*ae0998f1SAdriana Kobylak 
227*ae0998f1SAdriana Kobylak     // .get requires a non-const iterator
228*ae0998f1SAdriana Kobylak     for (auto& iter : data["lids"])
229*ae0998f1SAdriana Kobylak     {
230*ae0998f1SAdriana Kobylak         std::string name{};
231*ae0998f1SAdriana Kobylak         std::string lid{};
232*ae0998f1SAdriana Kobylak 
233*ae0998f1SAdriana Kobylak         try
234*ae0998f1SAdriana Kobylak         {
235*ae0998f1SAdriana Kobylak             name = iter["element_name"].get<std::string>();
236*ae0998f1SAdriana Kobylak             lid = iter["short_lid_name"].get<std::string>();
237*ae0998f1SAdriana Kobylak         }
238*ae0998f1SAdriana Kobylak         catch (std::exception& e)
239*ae0998f1SAdriana Kobylak         {
240*ae0998f1SAdriana Kobylak             // Possibly the element or lid name field was not found
241*ae0998f1SAdriana Kobylak             log<level::ERR>("Error reading JSON field",
242*ae0998f1SAdriana Kobylak                             entry("FILE=%s", elementsJsonFilePath.c_str()),
243*ae0998f1SAdriana Kobylak                             entry("ERROR=%s", e.what()));
244*ae0998f1SAdriana Kobylak             continue;
245*ae0998f1SAdriana Kobylak         }
246*ae0998f1SAdriana Kobylak 
247*ae0998f1SAdriana Kobylak         // The elements with the ipl extension have higher priority. Therefore
248*ae0998f1SAdriana Kobylak         // Use operator[] to overwrite value if an entry for it already exists.
249*ae0998f1SAdriana Kobylak         // Ex: if the JSON contains an entry A.P10 followed by A.P10.iplTime,
250*ae0998f1SAdriana Kobylak         // the lid value for the latter one will be overwrite the value of the
251*ae0998f1SAdriana Kobylak         // first one.
252*ae0998f1SAdriana Kobylak         constexpr auto iplExtension = ".iplTime";
253*ae0998f1SAdriana Kobylak         std::filesystem::path path(name);
254*ae0998f1SAdriana Kobylak         if (path.extension() == iplExtension)
255*ae0998f1SAdriana Kobylak         {
256*ae0998f1SAdriana Kobylak             // Some elements have an additional extension, ex: .P10.iplTime
257*ae0998f1SAdriana Kobylak             // Strip off the ipl extension with stem(), then check if there is
258*ae0998f1SAdriana Kobylak             // an additional extension with extension().
259*ae0998f1SAdriana Kobylak             if (!path.stem().extension().empty())
260*ae0998f1SAdriana Kobylak             {
261*ae0998f1SAdriana Kobylak                 // Check if the extension matches the extensions for this system
262*ae0998f1SAdriana Kobylak                 if (std::find(extensions.begin(), extensions.end(),
263*ae0998f1SAdriana Kobylak                               path.stem().extension()) == extensions.end())
264*ae0998f1SAdriana Kobylak                 {
265*ae0998f1SAdriana Kobylak                     continue;
266*ae0998f1SAdriana Kobylak                 }
267*ae0998f1SAdriana Kobylak             }
268*ae0998f1SAdriana Kobylak             // Get the element name without extensions by calling stem() twice
269*ae0998f1SAdriana Kobylak             // since stem() returns the base name if no periods are found.
270*ae0998f1SAdriana Kobylak             // Therefore both "element.P10" and "element.P10.iplTime" would
271*ae0998f1SAdriana Kobylak             // become "element".
272*ae0998f1SAdriana Kobylak             attr[path.stem().stem()] = lid;
273*ae0998f1SAdriana Kobylak             continue;
274*ae0998f1SAdriana Kobylak         }
275*ae0998f1SAdriana Kobylak 
276*ae0998f1SAdriana Kobylak         // Process all other extensions. The extension should match the list of
277*ae0998f1SAdriana Kobylak         // supported extensions for this system. Use .insert() to only add
278*ae0998f1SAdriana Kobylak         // entries that do not exist, so to not overwrite the values that may
279*ae0998f1SAdriana Kobylak         // had been added that had the ipl extension.
280*ae0998f1SAdriana Kobylak         if (std::find(extensions.begin(), extensions.end(), path.extension()) !=
281*ae0998f1SAdriana Kobylak             extensions.end())
282*ae0998f1SAdriana Kobylak         {
283*ae0998f1SAdriana Kobylak             attr.insert({path.stem(), lid});
284*ae0998f1SAdriana Kobylak         }
285*ae0998f1SAdriana Kobylak     }
286*ae0998f1SAdriana Kobylak     for (const auto& a : attr)
287*ae0998f1SAdriana Kobylak     {
288*ae0998f1SAdriana Kobylak         // Build the bios attribute string with format:
289*ae0998f1SAdriana Kobylak         // "element1=lid1,element2=lid2,elementN=lidN,"
290*ae0998f1SAdriana Kobylak         biosAttrStr += a.first + "=" + a.second + ",";
291*ae0998f1SAdriana Kobylak     }
292*ae0998f1SAdriana Kobylak 
293*ae0998f1SAdriana Kobylak     return biosAttrStr;
294*ae0998f1SAdriana Kobylak }
295*ae0998f1SAdriana Kobylak 
296*ae0998f1SAdriana Kobylak /**
297*ae0998f1SAdriana Kobylak  * @brief Set the bios attribute table with details of the host firmware data
298*ae0998f1SAdriana Kobylak  * for this system.
299*ae0998f1SAdriana Kobylak  *
300*ae0998f1SAdriana Kobylak  * @param[in] elementsJsonFilePath - The path to the host firmware json file.
301*ae0998f1SAdriana Kobylak  * @param[in] extentions - The extensions of the firmware blob files.
302*ae0998f1SAdriana Kobylak  */
303*ae0998f1SAdriana Kobylak void setBiosAttr(const std::filesystem::path& elementsJsonFilePath,
304*ae0998f1SAdriana Kobylak                  const std::vector<std::string>& extensions)
305*ae0998f1SAdriana Kobylak {
306*ae0998f1SAdriana Kobylak     auto biosAttrStr = getBiosAttrStr(elementsJsonFilePath, extensions);
307*ae0998f1SAdriana Kobylak 
30856f538caSAdriana Kobylak     constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager";
30956f538caSAdriana Kobylak     constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager";
31056f538caSAdriana Kobylak     constexpr auto dbusAttrName = "hb_lid_ids";
31156f538caSAdriana Kobylak     constexpr auto dbusAttrType =
31256f538caSAdriana Kobylak         "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
31356f538caSAdriana Kobylak 
31456f538caSAdriana Kobylak     using PendingAttributesType = std::vector<std::pair<
31556f538caSAdriana Kobylak         std::string, std::tuple<std::string, std::variant<std::string>>>>;
31656f538caSAdriana Kobylak     PendingAttributesType pendingAttributes;
31756f538caSAdriana Kobylak     pendingAttributes.emplace_back(std::make_pair(
31856f538caSAdriana Kobylak         dbusAttrName, std::make_tuple(dbusAttrType, biosAttrStr)));
31956f538caSAdriana Kobylak 
32056f538caSAdriana Kobylak     auto bus = sdbusplus::bus::new_default();
32156f538caSAdriana Kobylak     auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
32256f538caSAdriana Kobylak                                       MAPPER_INTERFACE, "GetObject");
32356f538caSAdriana Kobylak     method.append(biosConfigPath, std::vector<std::string>({biosConfigIntf}));
32456f538caSAdriana Kobylak     std::vector<std::pair<std::string, std::vector<std::string>>> response;
32556f538caSAdriana Kobylak     try
32656f538caSAdriana Kobylak     {
32756f538caSAdriana Kobylak         auto reply = bus.call(method);
32856f538caSAdriana Kobylak         reply.read(response);
32956f538caSAdriana Kobylak         if (response.empty())
33056f538caSAdriana Kobylak         {
33156f538caSAdriana Kobylak             log<level::ERR>("Error reading mapper response",
33256f538caSAdriana Kobylak                             entry("PATH=%s", biosConfigPath),
33356f538caSAdriana Kobylak                             entry("INTERFACE=%s", biosConfigIntf));
33456f538caSAdriana Kobylak             return;
33556f538caSAdriana Kobylak         }
33656f538caSAdriana Kobylak         auto method = bus.new_method_call((response.begin()->first).c_str(),
33756f538caSAdriana Kobylak                                           biosConfigPath,
33856f538caSAdriana Kobylak                                           SYSTEMD_PROPERTY_INTERFACE, "Set");
33956f538caSAdriana Kobylak         method.append(biosConfigIntf, "PendingAttributes",
34056f538caSAdriana Kobylak                       std::variant<PendingAttributesType>(pendingAttributes));
34156f538caSAdriana Kobylak         bus.call(method);
34256f538caSAdriana Kobylak     }
34356f538caSAdriana Kobylak     catch (const sdbusplus::exception::SdBusError& e)
34456f538caSAdriana Kobylak     {
34556f538caSAdriana Kobylak         log<level::ERR>("Error setting the bios attribute",
34656f538caSAdriana Kobylak                         entry("ERROR=%s", e.what()),
34756f538caSAdriana Kobylak                         entry("ATTRIBUTE=%s", dbusAttrName));
34856f538caSAdriana Kobylak         return;
34956f538caSAdriana Kobylak     }
35056f538caSAdriana Kobylak }
35153a27395SAdriana Kobylak 
35253a27395SAdriana Kobylak /**
353099543e4SBrad Bishop  * @brief Make callbacks on
354099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
355099543e4SBrad Bishop  *
356099543e4SBrad Bishop  * Look for an instance of
357099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
358099543e4SBrad Bishop  * argument and if found, issue the provided callback.
359099543e4SBrad Bishop  *
360099543e4SBrad Bishop  * @param[in] interfacesAndProperties the interfaces in which to look for an
361099543e4SBrad Bishop  * instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem
362099543e4SBrad Bishop  * @param[in] callback the user callback to make if
363099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
364099543e4SBrad Bishop  * interfacesAndProperties
365099543e4SBrad Bishop  * @return true if interfacesAndProperties contained an instance of
366099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
367099543e4SBrad Bishop  */
368099543e4SBrad Bishop bool maybeCall(const std::map<std::string,
369099543e4SBrad Bishop                               std::map<std::string,
370099543e4SBrad Bishop                                        std::variant<std::vector<std::string>>>>&
371099543e4SBrad Bishop                    interfacesAndProperties,
372099543e4SBrad Bishop                const MaybeCallCallbackType& callback)
373099543e4SBrad Bishop {
374099543e4SBrad Bishop     using namespace std::string_literals;
375099543e4SBrad Bishop 
376099543e4SBrad Bishop     static const auto interfaceName =
377099543e4SBrad Bishop         "xyz.openbmc_project.Configuration.IBMCompatibleSystem"s;
378099543e4SBrad Bishop     auto interfaceIterator = interfacesAndProperties.find(interfaceName);
379099543e4SBrad Bishop     if (interfaceIterator == interfacesAndProperties.cend())
380099543e4SBrad Bishop     {
381099543e4SBrad Bishop         // IBMCompatibleSystem interface not found, so instruct the caller to
382099543e4SBrad Bishop         // keep waiting or try again later.
383099543e4SBrad Bishop         return false;
384099543e4SBrad Bishop     }
385099543e4SBrad Bishop     auto propertyIterator = interfaceIterator->second.find("Names"s);
386099543e4SBrad Bishop     if (propertyIterator == interfaceIterator->second.cend())
387099543e4SBrad Bishop     {
388099543e4SBrad Bishop         // The interface exists but the property doesn't.  This is a bug in the
389099543e4SBrad Bishop         // IBMCompatibleSystem implementation.  The caller should not try
390099543e4SBrad Bishop         // again.
391099543e4SBrad Bishop         std::cerr << "Names property not implemented on " << interfaceName
392099543e4SBrad Bishop                   << "\n";
393099543e4SBrad Bishop         return true;
394099543e4SBrad Bishop     }
395099543e4SBrad Bishop 
396099543e4SBrad Bishop     const auto& ibmCompatibleSystem =
397099543e4SBrad Bishop         std::get<std::vector<std::string>>(propertyIterator->second);
398099543e4SBrad Bishop     if (callback)
399099543e4SBrad Bishop     {
400099543e4SBrad Bishop         callback(ibmCompatibleSystem);
401099543e4SBrad Bishop     }
402099543e4SBrad Bishop 
403099543e4SBrad Bishop     // IBMCompatibleSystem found and callback issued.
404099543e4SBrad Bishop     return true;
405099543e4SBrad Bishop }
406099543e4SBrad Bishop 
407099543e4SBrad Bishop /**
408099543e4SBrad Bishop  * @brief Make callbacks on
409099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
410099543e4SBrad Bishop  *
411099543e4SBrad Bishop  * Look for an instance of
412099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
413099543e4SBrad Bishop  * argument and if found, issue the provided callback.
414099543e4SBrad Bishop  *
415099543e4SBrad Bishop  * @param[in] message the DBus message in which to look for an instance of
416099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem
417099543e4SBrad Bishop  * @param[in] callback the user callback to make if
418099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
419099543e4SBrad Bishop  * message
420099543e4SBrad Bishop  * @return true if message contained an instance of
421099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
422099543e4SBrad Bishop  */
423099543e4SBrad Bishop bool maybeCallMessage(sdbusplus::message::message& message,
424099543e4SBrad Bishop                       const MaybeCallCallbackType& callback)
425099543e4SBrad Bishop {
426099543e4SBrad Bishop     std::map<std::string,
427099543e4SBrad Bishop              std::map<std::string, std::variant<std::vector<std::string>>>>
428099543e4SBrad Bishop         interfacesAndProperties;
429099543e4SBrad Bishop     sdbusplus::message::object_path _;
430099543e4SBrad Bishop     message.read(_, interfacesAndProperties);
431099543e4SBrad Bishop     return maybeCall(interfacesAndProperties, callback);
432099543e4SBrad Bishop }
433099543e4SBrad Bishop 
434099543e4SBrad Bishop /**
435099543e4SBrad Bishop  * @brief Determine system support for host firmware well-known names.
436099543e4SBrad Bishop  *
437099543e4SBrad Bishop  * Using the provided extensionMap and
438099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if
439099543e4SBrad Bishop  * well-known names for host firmare blob files are necessary and if so, create
440099543e4SBrad Bishop  * them.
441099543e4SBrad Bishop  *
442099543e4SBrad Bishop  * @param[in] extensionMap a map of
443099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
444099543e4SBrad Bishop  * file extensions.
445099543e4SBrad Bishop  * @param[in] hostFirmwareDirectory The directory in which findLinks should
446099543e4SBrad Bishop  * look for host firmware blob files that need well-known names.
447099543e4SBrad Bishop  * @param[in] ibmCompatibleSystem The names property of an instance of
448099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem
449099543e4SBrad Bishop  * @param[in] errorCallback A callback made in the event of filesystem errors.
450099543e4SBrad Bishop  */
451099543e4SBrad Bishop void maybeMakeLinks(
452099543e4SBrad Bishop     const std::map<std::string, std::vector<std::string>>& extensionMap,
453099543e4SBrad Bishop     const std::filesystem::path& hostFirmwareDirectory,
454099543e4SBrad Bishop     const std::vector<std::string>& ibmCompatibleSystem,
455099543e4SBrad Bishop     const ErrorCallbackType& errorCallback)
456099543e4SBrad Bishop {
457099543e4SBrad Bishop     std::vector<std::string> extensions;
458099543e4SBrad Bishop     if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
459099543e4SBrad Bishop                                             extensions))
460099543e4SBrad Bishop     {
461099543e4SBrad Bishop         findLinks(hostFirmwareDirectory, extensions, errorCallback, writeLink);
462099543e4SBrad Bishop     }
463099543e4SBrad Bishop }
464099543e4SBrad Bishop 
465099543e4SBrad Bishop /**
46653a27395SAdriana Kobylak  * @brief Determine system support for updating the bios attribute table.
46753a27395SAdriana Kobylak  *
46853a27395SAdriana Kobylak  * Using the provided extensionMap and
46953a27395SAdriana Kobylak  * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if the bios
47053a27395SAdriana Kobylak  * attribute table needs to be updated.
47153a27395SAdriana Kobylak  *
47253a27395SAdriana Kobylak  * @param[in] extensionMap a map of
47353a27395SAdriana Kobylak  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
47453a27395SAdriana Kobylak  * file extensions.
475*ae0998f1SAdriana Kobylak  * @param[in] elementsJsonFilePath The file path to the json file
47653a27395SAdriana Kobylak  * @param[in] ibmCompatibleSystem The names property of an instance of
47753a27395SAdriana Kobylak  * xyz.openbmc_project.Configuration.IBMCompatibleSystem
47853a27395SAdriana Kobylak  */
47953a27395SAdriana Kobylak void maybeSetBiosAttr(
48053a27395SAdriana Kobylak     const std::map<std::string, std::vector<std::string>>& extensionMap,
481*ae0998f1SAdriana Kobylak     const std::filesystem::path& elementsJsonFilePath,
48253a27395SAdriana Kobylak     const std::vector<std::string>& ibmCompatibleSystem)
48353a27395SAdriana Kobylak {
48453a27395SAdriana Kobylak     std::vector<std::string> extensions;
48553a27395SAdriana Kobylak     if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
48653a27395SAdriana Kobylak                                             extensions))
48753a27395SAdriana Kobylak     {
488*ae0998f1SAdriana Kobylak         setBiosAttr(elementsJsonFilePath, extensions);
48953a27395SAdriana Kobylak     }
49053a27395SAdriana Kobylak }
49153a27395SAdriana Kobylak 
49253a27395SAdriana Kobylak /**
493099543e4SBrad Bishop  * @brief process host firmware
494099543e4SBrad Bishop  *
495099543e4SBrad Bishop  * Allocate a callback context and register for DBus.ObjectManager Interfaces
496099543e4SBrad Bishop  * added signals from entity manager.
497099543e4SBrad Bishop  *
498099543e4SBrad Bishop  * Check the current entity manager object tree for a
499099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem instance (entity
500099543e4SBrad Bishop  * manager will be dbus activated if it is not running).  If one is found,
501099543e4SBrad Bishop  * determine if symlinks need to be created and create them.  Instruct the
502099543e4SBrad Bishop  * program event loop to exit.
503099543e4SBrad Bishop  *
504099543e4SBrad Bishop  * If no instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
505099543e4SBrad Bishop  * found return the callback context to main, where the program will sleep
506099543e4SBrad Bishop  * until the callback is invoked one or more times and instructs the program
507099543e4SBrad Bishop  * event loop to exit when
508099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem is added.
509099543e4SBrad Bishop  *
510099543e4SBrad Bishop  * @param[in] bus a DBus client connection
511099543e4SBrad Bishop  * @param[in] extensionMap a map of
512099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
513099543e4SBrad Bishop  * file extensions.
514099543e4SBrad Bishop  * @param[in] hostFirmwareDirectory The directory in which processHostFirmware
515099543e4SBrad Bishop  * should look for blob files.
516099543e4SBrad Bishop  * @param[in] errorCallback A callback made in the event of filesystem errors.
517099543e4SBrad Bishop  * @param[in] loop a program event loop
518099543e4SBrad Bishop  * @return nullptr if an instance of
519099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found, otherwise a
520099543e4SBrad Bishop  * pointer to an sdbusplus match object.
521099543e4SBrad Bishop  */
522099543e4SBrad Bishop std::shared_ptr<void> processHostFirmware(
523099543e4SBrad Bishop     sdbusplus::bus::bus& bus,
524099543e4SBrad Bishop     std::map<std::string, std::vector<std::string>> extensionMap,
525099543e4SBrad Bishop     std::filesystem::path hostFirmwareDirectory,
526099543e4SBrad Bishop     ErrorCallbackType errorCallback, sdeventplus::Event& loop)
527099543e4SBrad Bishop {
528099543e4SBrad Bishop     // ownership of extensionMap, hostFirmwareDirectory and errorCallback can't
529099543e4SBrad Bishop     // be transfered to the match callback because they are needed in the non
530099543e4SBrad Bishop     // async part of this function below, so they need to be moved to the heap.
531099543e4SBrad Bishop     auto pExtensionMap =
532099543e4SBrad Bishop         std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
533099543e4SBrad Bishop     auto pHostFirmwareDirectory =
534099543e4SBrad Bishop         std::make_shared<decltype(hostFirmwareDirectory)>(
535099543e4SBrad Bishop             std::move(hostFirmwareDirectory));
536099543e4SBrad Bishop     auto pErrorCallback =
537099543e4SBrad Bishop         std::make_shared<decltype(errorCallback)>(std::move(errorCallback));
538099543e4SBrad Bishop 
539099543e4SBrad Bishop     // register for a callback in case the IBMCompatibleSystem interface has
540099543e4SBrad Bishop     // not yet been published by entity manager.
541099543e4SBrad Bishop     auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match::match>(
542099543e4SBrad Bishop         bus,
543099543e4SBrad Bishop         sdbusplus::bus::match::rules::interfacesAdded() +
544099543e4SBrad Bishop             sdbusplus::bus::match::rules::sender(
545099543e4SBrad Bishop                 "xyz.openbmc_project.EntityManager"),
546099543e4SBrad Bishop         [pExtensionMap, pHostFirmwareDirectory, pErrorCallback,
547099543e4SBrad Bishop          &loop](auto& message) {
548099543e4SBrad Bishop             // bind the extension map, host firmware directory, and error
549099543e4SBrad Bishop             // callback to the maybeMakeLinks function.
550099543e4SBrad Bishop             auto maybeMakeLinksWithArgsBound =
551099543e4SBrad Bishop                 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
552099543e4SBrad Bishop                           std::cref(*pHostFirmwareDirectory),
553099543e4SBrad Bishop                           std::placeholders::_1, std::cref(*pErrorCallback));
554099543e4SBrad Bishop 
555099543e4SBrad Bishop             // if the InterfacesAdded message contains an an instance of
556099543e4SBrad Bishop             // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to
557099543e4SBrad Bishop             // see if links are necessary on this system and if so, create
558099543e4SBrad Bishop             // them.
559099543e4SBrad Bishop             if (maybeCallMessage(message, maybeMakeLinksWithArgsBound))
560099543e4SBrad Bishop             {
561099543e4SBrad Bishop                 // The IBMCompatibleSystem interface was found and the links
562099543e4SBrad Bishop                 // were created if applicable.  Instruct the event loop /
563099543e4SBrad Bishop                 // subcommand to exit.
564099543e4SBrad Bishop                 loop.exit(0);
565099543e4SBrad Bishop             }
566099543e4SBrad Bishop         });
567099543e4SBrad Bishop 
568099543e4SBrad Bishop     // now that we'll get a callback in the event of an InterfacesAdded signal
569099543e4SBrad Bishop     // (potentially containing
570099543e4SBrad Bishop     // xyz.openbmc_project.Configuration.IBMCompatibleSystem), activate entity
571099543e4SBrad Bishop     // manager if it isn't running and enumerate its objects
572099543e4SBrad Bishop     auto getManagedObjects = bus.new_method_call(
573099543e4SBrad Bishop         "xyz.openbmc_project.EntityManager", "/",
574099543e4SBrad Bishop         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
575099543e4SBrad Bishop     std::map<std::string,
576099543e4SBrad Bishop              std::map<std::string, std::variant<std::vector<std::string>>>>
577099543e4SBrad Bishop         interfacesAndProperties;
578099543e4SBrad Bishop     std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)>
579099543e4SBrad Bishop         objects;
580c79fa915SAdriana Kobylak     try
581c79fa915SAdriana Kobylak     {
582c79fa915SAdriana Kobylak         auto reply = bus.call(getManagedObjects);
583099543e4SBrad Bishop         reply.read(objects);
584c79fa915SAdriana Kobylak     }
585c79fa915SAdriana Kobylak     catch (const sdbusplus::exception::SdBusError& e)
586c79fa915SAdriana Kobylak     {
587c79fa915SAdriana Kobylak         // Error querying the EntityManager interface. Return the match to have
588c79fa915SAdriana Kobylak         // the callback run if/when the interface appears in D-Bus.
589c79fa915SAdriana Kobylak         return interfacesAddedMatch;
590c79fa915SAdriana Kobylak     }
591099543e4SBrad Bishop 
592099543e4SBrad Bishop     // bind the extension map, host firmware directory, and error callback to
593099543e4SBrad Bishop     // the maybeMakeLinks function.
594099543e4SBrad Bishop     auto maybeMakeLinksWithArgsBound =
595099543e4SBrad Bishop         std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
596099543e4SBrad Bishop                   std::cref(*pHostFirmwareDirectory), std::placeholders::_1,
597099543e4SBrad Bishop                   std::cref(*pErrorCallback));
598099543e4SBrad Bishop 
599099543e4SBrad Bishop     for (const auto& pair : objects)
600099543e4SBrad Bishop     {
601099543e4SBrad Bishop         std::tie(std::ignore, interfacesAndProperties) = pair;
602099543e4SBrad Bishop         // if interfacesAndProperties contains an an instance of
603099543e4SBrad Bishop         // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to see
604099543e4SBrad Bishop         // if links are necessary on this system and if so, create them
605099543e4SBrad Bishop         if (maybeCall(interfacesAndProperties, maybeMakeLinksWithArgsBound))
606099543e4SBrad Bishop         {
607099543e4SBrad Bishop             // The IBMCompatibleSystem interface is already on the bus and the
608099543e4SBrad Bishop             // links were created if applicable.  Instruct the event loop to
609099543e4SBrad Bishop             // exit.
610099543e4SBrad Bishop             loop.exit(0);
611099543e4SBrad Bishop             // The match object isn't needed anymore, so destroy it on return.
612099543e4SBrad Bishop             return nullptr;
613099543e4SBrad Bishop         }
614099543e4SBrad Bishop     }
615099543e4SBrad Bishop 
616099543e4SBrad Bishop     // The IBMCompatibleSystem interface has not yet been published.  Move
617099543e4SBrad Bishop     // ownership of the match callback to the caller.
618099543e4SBrad Bishop     return interfacesAddedMatch;
619099543e4SBrad Bishop }
62053a27395SAdriana Kobylak 
62153a27395SAdriana Kobylak /**
62253a27395SAdriana Kobylak  * @brief Update the Bios Attribute Table
62353a27395SAdriana Kobylak  *
62453a27395SAdriana Kobylak  * If an instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
62553a27395SAdriana Kobylak  * found, update the Bios Attribute Table with the appropriate host firmware
62653a27395SAdriana Kobylak  * data.
62753a27395SAdriana Kobylak  *
62853a27395SAdriana Kobylak  * @param[in] bus - D-Bus client connection.
62953a27395SAdriana Kobylak  * @param[in] extensionMap - Map of IBMCompatibleSystem names and host firmware
63053a27395SAdriana Kobylak  *                           file extensions.
631*ae0998f1SAdriana Kobylak  * @param[in] elementsJsonFilePath - The Path to the json file
63253a27395SAdriana Kobylak  * @param[in] loop - Program event loop.
63353a27395SAdriana Kobylak  * @return nullptr
63453a27395SAdriana Kobylak  */
63553a27395SAdriana Kobylak std::shared_ptr<void> updateBiosAttrTable(
63653a27395SAdriana Kobylak     sdbusplus::bus::bus& bus,
63753a27395SAdriana Kobylak     std::map<std::string, std::vector<std::string>> extensionMap,
638*ae0998f1SAdriana Kobylak     std::filesystem::path elementsJsonFilePath, sdeventplus::Event& loop)
63953a27395SAdriana Kobylak {
64053a27395SAdriana Kobylak     auto pExtensionMap =
64153a27395SAdriana Kobylak         std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
642*ae0998f1SAdriana Kobylak     auto pElementsJsonFilePath =
643*ae0998f1SAdriana Kobylak         std::make_shared<decltype(elementsJsonFilePath)>(
644*ae0998f1SAdriana Kobylak             std::move(elementsJsonFilePath));
64553a27395SAdriana Kobylak 
64653a27395SAdriana Kobylak     auto getManagedObjects = bus.new_method_call(
64753a27395SAdriana Kobylak         "xyz.openbmc_project.EntityManager", "/",
64853a27395SAdriana Kobylak         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
64953a27395SAdriana Kobylak     std::map<std::string,
65053a27395SAdriana Kobylak              std::map<std::string, std::variant<std::vector<std::string>>>>
65153a27395SAdriana Kobylak         interfacesAndProperties;
65253a27395SAdriana Kobylak     std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)>
65353a27395SAdriana Kobylak         objects;
65453a27395SAdriana Kobylak     try
65553a27395SAdriana Kobylak     {
65653a27395SAdriana Kobylak         auto reply = bus.call(getManagedObjects);
65753a27395SAdriana Kobylak         reply.read(objects);
65853a27395SAdriana Kobylak     }
65953a27395SAdriana Kobylak     catch (const sdbusplus::exception::SdBusError& e)
66053a27395SAdriana Kobylak     {}
66153a27395SAdriana Kobylak 
662*ae0998f1SAdriana Kobylak     auto maybeSetAttrWithArgsBound =
663*ae0998f1SAdriana Kobylak         std::bind(maybeSetBiosAttr, std::cref(*pExtensionMap),
664*ae0998f1SAdriana Kobylak                   std::cref(*pElementsJsonFilePath), std::placeholders::_1);
66553a27395SAdriana Kobylak 
66653a27395SAdriana Kobylak     for (const auto& pair : objects)
66753a27395SAdriana Kobylak     {
66853a27395SAdriana Kobylak         std::tie(std::ignore, interfacesAndProperties) = pair;
66953a27395SAdriana Kobylak         if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound))
67053a27395SAdriana Kobylak         {
67153a27395SAdriana Kobylak             break;
67253a27395SAdriana Kobylak         }
67353a27395SAdriana Kobylak     }
67453a27395SAdriana Kobylak 
67553a27395SAdriana Kobylak     loop.exit(0);
67653a27395SAdriana Kobylak     return nullptr;
67753a27395SAdriana Kobylak }
67853a27395SAdriana Kobylak 
679099543e4SBrad Bishop } // namespace process_hostfirmware
680099543e4SBrad Bishop } // namespace functions
681