1099543e4SBrad Bishop // SPDX-License-Identifier: Apache-2.0
2099543e4SBrad Bishop 
3099543e4SBrad Bishop /**@file functions.cpp*/
4099543e4SBrad Bishop 
5*56f538caSAdriana Kobylak #include "config.h"
6*56f538caSAdriana Kobylak 
7099543e4SBrad Bishop #include "functions.hpp"
8099543e4SBrad Bishop 
9*56f538caSAdriana Kobylak #include <phosphor-logging/log.hpp>
10099543e4SBrad Bishop #include <sdbusplus/bus.hpp>
11099543e4SBrad Bishop #include <sdbusplus/bus/match.hpp>
12c79fa915SAdriana Kobylak #include <sdbusplus/exception.hpp>
13099543e4SBrad Bishop #include <sdbusplus/message.hpp>
14099543e4SBrad Bishop #include <sdeventplus/event.hpp>
15099543e4SBrad Bishop 
16099543e4SBrad Bishop #include <filesystem>
17099543e4SBrad Bishop #include <functional>
18099543e4SBrad Bishop #include <iostream>
19099543e4SBrad Bishop #include <map>
20099543e4SBrad Bishop #include <memory>
21099543e4SBrad Bishop #include <string>
22099543e4SBrad Bishop #include <variant>
23099543e4SBrad Bishop #include <vector>
24099543e4SBrad Bishop 
25099543e4SBrad Bishop namespace functions
26099543e4SBrad Bishop {
27099543e4SBrad Bishop namespace process_hostfirmware
28099543e4SBrad Bishop {
29099543e4SBrad Bishop 
30*56f538caSAdriana Kobylak using namespace phosphor::logging;
31*56f538caSAdriana Kobylak 
32099543e4SBrad Bishop /**
33099543e4SBrad Bishop  * @brief Issue callbacks safely
34099543e4SBrad Bishop  *
35099543e4SBrad Bishop  * std::function can be empty, so this wrapper method checks for that prior to
36099543e4SBrad Bishop  * calling it to avoid std::bad_function_call
37099543e4SBrad Bishop  *
38099543e4SBrad Bishop  * @tparam Sig the types of the std::function arguments
39099543e4SBrad Bishop  * @tparam Args the deduced argument types
40099543e4SBrad Bishop  * @param[in] callback the callback being wrapped
41099543e4SBrad Bishop  * @param[in] args the callback arguments
42099543e4SBrad Bishop  */
43099543e4SBrad Bishop template <typename... Sig, typename... Args>
44099543e4SBrad Bishop void makeCallback(const std::function<void(Sig...)>& callback, Args&&... args)
45099543e4SBrad Bishop {
46099543e4SBrad Bishop     if (callback)
47099543e4SBrad Bishop     {
48099543e4SBrad Bishop         callback(std::forward<Args>(args)...);
49099543e4SBrad Bishop     }
50099543e4SBrad Bishop }
51099543e4SBrad Bishop 
52099543e4SBrad Bishop /**
53099543e4SBrad Bishop  * @brief Get file extensions for IBMCompatibleSystem
54099543e4SBrad Bishop  *
55099543e4SBrad Bishop  * IBM host firmware can be deployed as blobs (files) in a filesystem.  Host
56099543e4SBrad Bishop  * firmware blobs for different values of
57099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem are packaged with
58099543e4SBrad Bishop  * different filename extensions.  getExtensionsForIbmCompatibleSystem
59099543e4SBrad Bishop  * maintains the mapping from a given value of
60099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to an array of
61099543e4SBrad Bishop  * filename extensions.
62099543e4SBrad Bishop  *
63099543e4SBrad Bishop  * If a mapping is found getExtensionsForIbmCompatibleSystem returns true and
64099543e4SBrad Bishop  * the extensions parameter is reset with the map entry.  If no mapping is
65099543e4SBrad Bishop  * found getExtensionsForIbmCompatibleSystem returns false and extensions is
66099543e4SBrad Bishop  * unmodified.
67099543e4SBrad Bishop  *
68099543e4SBrad Bishop  * @param[in] extensionMap a map of
69099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
70099543e4SBrad Bishop  * file extensions.
71099543e4SBrad Bishop  * @param[in] ibmCompatibleSystem The names property of an instance of
72099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem
73099543e4SBrad Bishop  * @param[out] extentions the host firmware blob file extensions
74099543e4SBrad Bishop  * @return true if an entry was found, otherwise false
75099543e4SBrad Bishop  */
76099543e4SBrad Bishop bool getExtensionsForIbmCompatibleSystem(
77099543e4SBrad Bishop     const std::map<std::string, std::vector<std::string>>& extensionMap,
78099543e4SBrad Bishop     const std::vector<std::string>& ibmCompatibleSystem,
79099543e4SBrad Bishop     std::vector<std::string>& extensions)
80099543e4SBrad Bishop {
81099543e4SBrad Bishop     for (const auto& system : ibmCompatibleSystem)
82099543e4SBrad Bishop     {
83099543e4SBrad Bishop         auto extensionMapIterator = extensionMap.find(system);
84099543e4SBrad Bishop         if (extensionMapIterator != extensionMap.end())
85099543e4SBrad Bishop         {
86099543e4SBrad Bishop             extensions = extensionMapIterator->second;
87099543e4SBrad Bishop             return true;
88099543e4SBrad Bishop         }
89099543e4SBrad Bishop     }
90099543e4SBrad Bishop 
91099543e4SBrad Bishop     return false;
92099543e4SBrad Bishop }
93099543e4SBrad Bishop 
94099543e4SBrad Bishop /**
95099543e4SBrad Bishop  * @brief Write host firmware well-known name
96099543e4SBrad Bishop  *
97099543e4SBrad Bishop  * A wrapper around std::filesystem::create_symlink that avoids EEXIST by
98099543e4SBrad Bishop  * deleting any pre-existing file.
99099543e4SBrad Bishop  *
100099543e4SBrad Bishop  * @param[in] linkTarget The link target argument to
101099543e4SBrad Bishop  * std::filesystem::create_symlink
102099543e4SBrad Bishop  * @param[in] linkPath The link path argument to std::filesystem::create_symlink
103099543e4SBrad Bishop  * @param[in] errorCallback A callback made in the event of filesystem errors.
104099543e4SBrad Bishop  */
105099543e4SBrad Bishop void writeLink(const std::filesystem::path& linkTarget,
106099543e4SBrad Bishop                const std::filesystem::path& linkPath,
107099543e4SBrad Bishop                const ErrorCallbackType& errorCallback)
108099543e4SBrad Bishop {
109099543e4SBrad Bishop     std::error_code ec;
110099543e4SBrad Bishop 
111099543e4SBrad Bishop     // remove files with the same name as the symlink to be created,
112099543e4SBrad Bishop     // otherwise symlink will fail with EEXIST.
113099543e4SBrad Bishop     if (!std::filesystem::remove(linkPath, ec))
114099543e4SBrad Bishop     {
115099543e4SBrad Bishop         if (ec)
116099543e4SBrad Bishop         {
117099543e4SBrad Bishop             makeCallback(errorCallback, linkPath, ec);
118099543e4SBrad Bishop             return;
119099543e4SBrad Bishop         }
120099543e4SBrad Bishop     }
121099543e4SBrad Bishop 
122099543e4SBrad Bishop     std::filesystem::create_symlink(linkTarget, linkPath, ec);
123099543e4SBrad Bishop     if (ec)
124099543e4SBrad Bishop     {
125099543e4SBrad Bishop         makeCallback(errorCallback, linkPath, ec);
126099543e4SBrad Bishop         return;
127099543e4SBrad Bishop     }
128099543e4SBrad Bishop }
129099543e4SBrad Bishop 
130099543e4SBrad Bishop /**
131099543e4SBrad Bishop  * @brief Find host firmware blob files that need well-known names
132099543e4SBrad Bishop  *
133099543e4SBrad Bishop  * The IBM host firmware runtime looks for data and/or additional code while
134099543e4SBrad Bishop  * bootstraping in files with well-known names.  findLinks uses the provided
135099543e4SBrad Bishop  * extensions argument to find host firmware blob files that require a
136099543e4SBrad Bishop  * well-known name.  When a blob is found, issue the provided callback
137099543e4SBrad Bishop  * (typically a function that will write a symlink).
138099543e4SBrad Bishop  *
139099543e4SBrad Bishop  * @param[in] hostFirmwareDirectory The directory in which findLinks should
140099543e4SBrad Bishop  * look for host firmware blob files that need well-known names.
141099543e4SBrad Bishop  * @param[in] extentions The extensions of the firmware blob files denote a
142099543e4SBrad Bishop  * host firmware blob file requires a well-known name.
143099543e4SBrad Bishop  * @param[in] errorCallback A callback made in the event of filesystem errors.
144099543e4SBrad Bishop  * @param[in] linkCallback A callback made when host firmware blob files
145099543e4SBrad Bishop  * needing a well known name are found.
146099543e4SBrad Bishop  */
147099543e4SBrad Bishop void findLinks(const std::filesystem::path& hostFirmwareDirectory,
148099543e4SBrad Bishop                const std::vector<std::string>& extensions,
149099543e4SBrad Bishop                const ErrorCallbackType& errorCallback,
150099543e4SBrad Bishop                const LinkCallbackType& linkCallback)
151099543e4SBrad Bishop {
152099543e4SBrad Bishop     std::error_code ec;
153099543e4SBrad Bishop     std::filesystem::directory_iterator directoryIterator(hostFirmwareDirectory,
154099543e4SBrad Bishop                                                           ec);
155099543e4SBrad Bishop     if (ec)
156099543e4SBrad Bishop     {
157099543e4SBrad Bishop         makeCallback(errorCallback, hostFirmwareDirectory, ec);
158099543e4SBrad Bishop         return;
159099543e4SBrad Bishop     }
160099543e4SBrad Bishop 
161fdc91fa0SAdriana Kobylak     // Create a symlink from HBB to the corresponding LID file if it exists
162fdc91fa0SAdriana Kobylak     static const auto hbbLid = "81e0065a.lid";
163fdc91fa0SAdriana Kobylak     auto hbbLidPath = hostFirmwareDirectory / hbbLid;
164fdc91fa0SAdriana Kobylak     if (std::filesystem::exists(hbbLidPath))
165fdc91fa0SAdriana Kobylak     {
166fdc91fa0SAdriana Kobylak         static const auto hbbName = "HBB";
167fdc91fa0SAdriana Kobylak         auto hbbLinkPath = hostFirmwareDirectory / hbbName;
168fdc91fa0SAdriana Kobylak         makeCallback(linkCallback, hbbLid, hbbLinkPath, errorCallback);
169fdc91fa0SAdriana Kobylak     }
170fdc91fa0SAdriana Kobylak 
171099543e4SBrad Bishop     for (; directoryIterator != std::filesystem::end(directoryIterator);
172099543e4SBrad Bishop          directoryIterator.increment(ec))
173099543e4SBrad Bishop     {
174099543e4SBrad Bishop         const auto& file = directoryIterator->path();
175099543e4SBrad Bishop         if (ec)
176099543e4SBrad Bishop         {
177099543e4SBrad Bishop             makeCallback(errorCallback, file, ec);
178099543e4SBrad Bishop             // quit here if the increment call failed otherwise the loop may
179099543e4SBrad Bishop             // never finish
180099543e4SBrad Bishop             break;
181099543e4SBrad Bishop         }
182099543e4SBrad Bishop 
183099543e4SBrad Bishop         if (std::find(extensions.begin(), extensions.end(), file.extension()) ==
184099543e4SBrad Bishop             extensions.end())
185099543e4SBrad Bishop         {
186099543e4SBrad Bishop             // this file doesn't have an extension or doesn't match any of the
187099543e4SBrad Bishop             // provided extensions.
188099543e4SBrad Bishop             continue;
189099543e4SBrad Bishop         }
190099543e4SBrad Bishop 
191099543e4SBrad Bishop         auto linkPath(file.parent_path().append(
192099543e4SBrad Bishop             static_cast<const std::string&>(file.stem())));
193099543e4SBrad Bishop 
194099543e4SBrad Bishop         makeCallback(linkCallback, file.filename(), linkPath, errorCallback);
195099543e4SBrad Bishop     }
196099543e4SBrad Bishop }
197099543e4SBrad Bishop 
198099543e4SBrad Bishop /**
19953a27395SAdriana Kobylak  * @brief Set the bios attribute table with details of the host firmware data
20053a27395SAdriana Kobylak  * for this system.
20153a27395SAdriana Kobylak  */
20253a27395SAdriana Kobylak void setBiosAttr()
203*56f538caSAdriana Kobylak {
204*56f538caSAdriana Kobylak     std::string biosAttrStr{};
205*56f538caSAdriana Kobylak 
206*56f538caSAdriana Kobylak     constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager";
207*56f538caSAdriana Kobylak     constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager";
208*56f538caSAdriana Kobylak     constexpr auto dbusAttrName = "hb_lid_ids";
209*56f538caSAdriana Kobylak     constexpr auto dbusAttrType =
210*56f538caSAdriana Kobylak         "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
211*56f538caSAdriana Kobylak 
212*56f538caSAdriana Kobylak     using PendingAttributesType = std::vector<std::pair<
213*56f538caSAdriana Kobylak         std::string, std::tuple<std::string, std::variant<std::string>>>>;
214*56f538caSAdriana Kobylak     PendingAttributesType pendingAttributes;
215*56f538caSAdriana Kobylak     pendingAttributes.emplace_back(std::make_pair(
216*56f538caSAdriana Kobylak         dbusAttrName, std::make_tuple(dbusAttrType, biosAttrStr)));
217*56f538caSAdriana Kobylak 
218*56f538caSAdriana Kobylak     auto bus = sdbusplus::bus::new_default();
219*56f538caSAdriana Kobylak     auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
220*56f538caSAdriana Kobylak                                       MAPPER_INTERFACE, "GetObject");
221*56f538caSAdriana Kobylak     method.append(biosConfigPath, std::vector<std::string>({biosConfigIntf}));
222*56f538caSAdriana Kobylak     std::vector<std::pair<std::string, std::vector<std::string>>> response;
223*56f538caSAdriana Kobylak     try
224*56f538caSAdriana Kobylak     {
225*56f538caSAdriana Kobylak         auto reply = bus.call(method);
226*56f538caSAdriana Kobylak         reply.read(response);
227*56f538caSAdriana Kobylak         if (response.empty())
228*56f538caSAdriana Kobylak         {
229*56f538caSAdriana Kobylak             log<level::ERR>("Error reading mapper response",
230*56f538caSAdriana Kobylak                             entry("PATH=%s", biosConfigPath),
231*56f538caSAdriana Kobylak                             entry("INTERFACE=%s", biosConfigIntf));
232*56f538caSAdriana Kobylak             return;
233*56f538caSAdriana Kobylak         }
234*56f538caSAdriana Kobylak         auto method = bus.new_method_call((response.begin()->first).c_str(),
235*56f538caSAdriana Kobylak                                           biosConfigPath,
236*56f538caSAdriana Kobylak                                           SYSTEMD_PROPERTY_INTERFACE, "Set");
237*56f538caSAdriana Kobylak         method.append(biosConfigIntf, "PendingAttributes",
238*56f538caSAdriana Kobylak                       std::variant<PendingAttributesType>(pendingAttributes));
239*56f538caSAdriana Kobylak         bus.call(method);
240*56f538caSAdriana Kobylak     }
241*56f538caSAdriana Kobylak     catch (const sdbusplus::exception::SdBusError& e)
242*56f538caSAdriana Kobylak     {
243*56f538caSAdriana Kobylak         log<level::ERR>("Error setting the bios attribute",
244*56f538caSAdriana Kobylak                         entry("ERROR=%s", e.what()),
245*56f538caSAdriana Kobylak                         entry("ATTRIBUTE=%s", dbusAttrName));
246*56f538caSAdriana Kobylak         return;
247*56f538caSAdriana Kobylak     }
248*56f538caSAdriana Kobylak }
24953a27395SAdriana Kobylak 
25053a27395SAdriana Kobylak /**
251099543e4SBrad Bishop  * @brief Make callbacks on
252099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
253099543e4SBrad Bishop  *
254099543e4SBrad Bishop  * Look for an instance of
255099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
256099543e4SBrad Bishop  * argument and if found, issue the provided callback.
257099543e4SBrad Bishop  *
258099543e4SBrad Bishop  * @param[in] interfacesAndProperties the interfaces in which to look for an
259099543e4SBrad Bishop  * instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem
260099543e4SBrad Bishop  * @param[in] callback the user callback to make if
261099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
262099543e4SBrad Bishop  * interfacesAndProperties
263099543e4SBrad Bishop  * @return true if interfacesAndProperties contained an instance of
264099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
265099543e4SBrad Bishop  */
266099543e4SBrad Bishop bool maybeCall(const std::map<std::string,
267099543e4SBrad Bishop                               std::map<std::string,
268099543e4SBrad Bishop                                        std::variant<std::vector<std::string>>>>&
269099543e4SBrad Bishop                    interfacesAndProperties,
270099543e4SBrad Bishop                const MaybeCallCallbackType& callback)
271099543e4SBrad Bishop {
272099543e4SBrad Bishop     using namespace std::string_literals;
273099543e4SBrad Bishop 
274099543e4SBrad Bishop     static const auto interfaceName =
275099543e4SBrad Bishop         "xyz.openbmc_project.Configuration.IBMCompatibleSystem"s;
276099543e4SBrad Bishop     auto interfaceIterator = interfacesAndProperties.find(interfaceName);
277099543e4SBrad Bishop     if (interfaceIterator == interfacesAndProperties.cend())
278099543e4SBrad Bishop     {
279099543e4SBrad Bishop         // IBMCompatibleSystem interface not found, so instruct the caller to
280099543e4SBrad Bishop         // keep waiting or try again later.
281099543e4SBrad Bishop         return false;
282099543e4SBrad Bishop     }
283099543e4SBrad Bishop     auto propertyIterator = interfaceIterator->second.find("Names"s);
284099543e4SBrad Bishop     if (propertyIterator == interfaceIterator->second.cend())
285099543e4SBrad Bishop     {
286099543e4SBrad Bishop         // The interface exists but the property doesn't.  This is a bug in the
287099543e4SBrad Bishop         // IBMCompatibleSystem implementation.  The caller should not try
288099543e4SBrad Bishop         // again.
289099543e4SBrad Bishop         std::cerr << "Names property not implemented on " << interfaceName
290099543e4SBrad Bishop                   << "\n";
291099543e4SBrad Bishop         return true;
292099543e4SBrad Bishop     }
293099543e4SBrad Bishop 
294099543e4SBrad Bishop     const auto& ibmCompatibleSystem =
295099543e4SBrad Bishop         std::get<std::vector<std::string>>(propertyIterator->second);
296099543e4SBrad Bishop     if (callback)
297099543e4SBrad Bishop     {
298099543e4SBrad Bishop         callback(ibmCompatibleSystem);
299099543e4SBrad Bishop     }
300099543e4SBrad Bishop 
301099543e4SBrad Bishop     // IBMCompatibleSystem found and callback issued.
302099543e4SBrad Bishop     return true;
303099543e4SBrad Bishop }
304099543e4SBrad Bishop 
305099543e4SBrad Bishop /**
306099543e4SBrad Bishop  * @brief Make callbacks on
307099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
308099543e4SBrad Bishop  *
309099543e4SBrad Bishop  * Look for an instance of
310099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
311099543e4SBrad Bishop  * argument and if found, issue the provided callback.
312099543e4SBrad Bishop  *
313099543e4SBrad Bishop  * @param[in] message the DBus message in which to look for an instance of
314099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem
315099543e4SBrad Bishop  * @param[in] callback the user callback to make if
316099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
317099543e4SBrad Bishop  * message
318099543e4SBrad Bishop  * @return true if message contained an instance of
319099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
320099543e4SBrad Bishop  */
321099543e4SBrad Bishop bool maybeCallMessage(sdbusplus::message::message& message,
322099543e4SBrad Bishop                       const MaybeCallCallbackType& callback)
323099543e4SBrad Bishop {
324099543e4SBrad Bishop     std::map<std::string,
325099543e4SBrad Bishop              std::map<std::string, std::variant<std::vector<std::string>>>>
326099543e4SBrad Bishop         interfacesAndProperties;
327099543e4SBrad Bishop     sdbusplus::message::object_path _;
328099543e4SBrad Bishop     message.read(_, interfacesAndProperties);
329099543e4SBrad Bishop     return maybeCall(interfacesAndProperties, callback);
330099543e4SBrad Bishop }
331099543e4SBrad Bishop 
332099543e4SBrad Bishop /**
333099543e4SBrad Bishop  * @brief Determine system support for host firmware well-known names.
334099543e4SBrad Bishop  *
335099543e4SBrad Bishop  * Using the provided extensionMap and
336099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if
337099543e4SBrad Bishop  * well-known names for host firmare blob files are necessary and if so, create
338099543e4SBrad Bishop  * them.
339099543e4SBrad Bishop  *
340099543e4SBrad Bishop  * @param[in] extensionMap a map of
341099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
342099543e4SBrad Bishop  * file extensions.
343099543e4SBrad Bishop  * @param[in] hostFirmwareDirectory The directory in which findLinks should
344099543e4SBrad Bishop  * look for host firmware blob files that need well-known names.
345099543e4SBrad Bishop  * @param[in] ibmCompatibleSystem The names property of an instance of
346099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem
347099543e4SBrad Bishop  * @param[in] errorCallback A callback made in the event of filesystem errors.
348099543e4SBrad Bishop  */
349099543e4SBrad Bishop void maybeMakeLinks(
350099543e4SBrad Bishop     const std::map<std::string, std::vector<std::string>>& extensionMap,
351099543e4SBrad Bishop     const std::filesystem::path& hostFirmwareDirectory,
352099543e4SBrad Bishop     const std::vector<std::string>& ibmCompatibleSystem,
353099543e4SBrad Bishop     const ErrorCallbackType& errorCallback)
354099543e4SBrad Bishop {
355099543e4SBrad Bishop     std::vector<std::string> extensions;
356099543e4SBrad Bishop     if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
357099543e4SBrad Bishop                                             extensions))
358099543e4SBrad Bishop     {
359099543e4SBrad Bishop         findLinks(hostFirmwareDirectory, extensions, errorCallback, writeLink);
360099543e4SBrad Bishop     }
361099543e4SBrad Bishop }
362099543e4SBrad Bishop 
363099543e4SBrad Bishop /**
36453a27395SAdriana Kobylak  * @brief Determine system support for updating the bios attribute table.
36553a27395SAdriana Kobylak  *
36653a27395SAdriana Kobylak  * Using the provided extensionMap and
36753a27395SAdriana Kobylak  * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if the bios
36853a27395SAdriana Kobylak  * attribute table needs to be updated.
36953a27395SAdriana Kobylak  *
37053a27395SAdriana Kobylak  * @param[in] extensionMap a map of
37153a27395SAdriana Kobylak  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
37253a27395SAdriana Kobylak  * file extensions.
37353a27395SAdriana Kobylak  * @param[in] ibmCompatibleSystem The names property of an instance of
37453a27395SAdriana Kobylak  * xyz.openbmc_project.Configuration.IBMCompatibleSystem
37553a27395SAdriana Kobylak  */
37653a27395SAdriana Kobylak void maybeSetBiosAttr(
37753a27395SAdriana Kobylak     const std::map<std::string, std::vector<std::string>>& extensionMap,
37853a27395SAdriana Kobylak     const std::vector<std::string>& ibmCompatibleSystem)
37953a27395SAdriana Kobylak {
38053a27395SAdriana Kobylak     std::vector<std::string> extensions;
38153a27395SAdriana Kobylak     if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
38253a27395SAdriana Kobylak                                             extensions))
38353a27395SAdriana Kobylak     {
38453a27395SAdriana Kobylak         setBiosAttr();
38553a27395SAdriana Kobylak     }
38653a27395SAdriana Kobylak }
38753a27395SAdriana Kobylak 
38853a27395SAdriana Kobylak /**
389099543e4SBrad Bishop  * @brief process host firmware
390099543e4SBrad Bishop  *
391099543e4SBrad Bishop  * Allocate a callback context and register for DBus.ObjectManager Interfaces
392099543e4SBrad Bishop  * added signals from entity manager.
393099543e4SBrad Bishop  *
394099543e4SBrad Bishop  * Check the current entity manager object tree for a
395099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem instance (entity
396099543e4SBrad Bishop  * manager will be dbus activated if it is not running).  If one is found,
397099543e4SBrad Bishop  * determine if symlinks need to be created and create them.  Instruct the
398099543e4SBrad Bishop  * program event loop to exit.
399099543e4SBrad Bishop  *
400099543e4SBrad Bishop  * If no instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
401099543e4SBrad Bishop  * found return the callback context to main, where the program will sleep
402099543e4SBrad Bishop  * until the callback is invoked one or more times and instructs the program
403099543e4SBrad Bishop  * event loop to exit when
404099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem is added.
405099543e4SBrad Bishop  *
406099543e4SBrad Bishop  * @param[in] bus a DBus client connection
407099543e4SBrad Bishop  * @param[in] extensionMap a map of
408099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
409099543e4SBrad Bishop  * file extensions.
410099543e4SBrad Bishop  * @param[in] hostFirmwareDirectory The directory in which processHostFirmware
411099543e4SBrad Bishop  * should look for blob files.
412099543e4SBrad Bishop  * @param[in] errorCallback A callback made in the event of filesystem errors.
413099543e4SBrad Bishop  * @param[in] loop a program event loop
414099543e4SBrad Bishop  * @return nullptr if an instance of
415099543e4SBrad Bishop  * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found, otherwise a
416099543e4SBrad Bishop  * pointer to an sdbusplus match object.
417099543e4SBrad Bishop  */
418099543e4SBrad Bishop std::shared_ptr<void> processHostFirmware(
419099543e4SBrad Bishop     sdbusplus::bus::bus& bus,
420099543e4SBrad Bishop     std::map<std::string, std::vector<std::string>> extensionMap,
421099543e4SBrad Bishop     std::filesystem::path hostFirmwareDirectory,
422099543e4SBrad Bishop     ErrorCallbackType errorCallback, sdeventplus::Event& loop)
423099543e4SBrad Bishop {
424099543e4SBrad Bishop     // ownership of extensionMap, hostFirmwareDirectory and errorCallback can't
425099543e4SBrad Bishop     // be transfered to the match callback because they are needed in the non
426099543e4SBrad Bishop     // async part of this function below, so they need to be moved to the heap.
427099543e4SBrad Bishop     auto pExtensionMap =
428099543e4SBrad Bishop         std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
429099543e4SBrad Bishop     auto pHostFirmwareDirectory =
430099543e4SBrad Bishop         std::make_shared<decltype(hostFirmwareDirectory)>(
431099543e4SBrad Bishop             std::move(hostFirmwareDirectory));
432099543e4SBrad Bishop     auto pErrorCallback =
433099543e4SBrad Bishop         std::make_shared<decltype(errorCallback)>(std::move(errorCallback));
434099543e4SBrad Bishop 
435099543e4SBrad Bishop     // register for a callback in case the IBMCompatibleSystem interface has
436099543e4SBrad Bishop     // not yet been published by entity manager.
437099543e4SBrad Bishop     auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match::match>(
438099543e4SBrad Bishop         bus,
439099543e4SBrad Bishop         sdbusplus::bus::match::rules::interfacesAdded() +
440099543e4SBrad Bishop             sdbusplus::bus::match::rules::sender(
441099543e4SBrad Bishop                 "xyz.openbmc_project.EntityManager"),
442099543e4SBrad Bishop         [pExtensionMap, pHostFirmwareDirectory, pErrorCallback,
443099543e4SBrad Bishop          &loop](auto& message) {
444099543e4SBrad Bishop             // bind the extension map, host firmware directory, and error
445099543e4SBrad Bishop             // callback to the maybeMakeLinks function.
446099543e4SBrad Bishop             auto maybeMakeLinksWithArgsBound =
447099543e4SBrad Bishop                 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
448099543e4SBrad Bishop                           std::cref(*pHostFirmwareDirectory),
449099543e4SBrad Bishop                           std::placeholders::_1, std::cref(*pErrorCallback));
450099543e4SBrad Bishop 
451099543e4SBrad Bishop             // if the InterfacesAdded message contains an an instance of
452099543e4SBrad Bishop             // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to
453099543e4SBrad Bishop             // see if links are necessary on this system and if so, create
454099543e4SBrad Bishop             // them.
455099543e4SBrad Bishop             if (maybeCallMessage(message, maybeMakeLinksWithArgsBound))
456099543e4SBrad Bishop             {
457099543e4SBrad Bishop                 // The IBMCompatibleSystem interface was found and the links
458099543e4SBrad Bishop                 // were created if applicable.  Instruct the event loop /
459099543e4SBrad Bishop                 // subcommand to exit.
460099543e4SBrad Bishop                 loop.exit(0);
461099543e4SBrad Bishop             }
462099543e4SBrad Bishop         });
463099543e4SBrad Bishop 
464099543e4SBrad Bishop     // now that we'll get a callback in the event of an InterfacesAdded signal
465099543e4SBrad Bishop     // (potentially containing
466099543e4SBrad Bishop     // xyz.openbmc_project.Configuration.IBMCompatibleSystem), activate entity
467099543e4SBrad Bishop     // manager if it isn't running and enumerate its objects
468099543e4SBrad Bishop     auto getManagedObjects = bus.new_method_call(
469099543e4SBrad Bishop         "xyz.openbmc_project.EntityManager", "/",
470099543e4SBrad Bishop         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
471099543e4SBrad Bishop     std::map<std::string,
472099543e4SBrad Bishop              std::map<std::string, std::variant<std::vector<std::string>>>>
473099543e4SBrad Bishop         interfacesAndProperties;
474099543e4SBrad Bishop     std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)>
475099543e4SBrad Bishop         objects;
476c79fa915SAdriana Kobylak     try
477c79fa915SAdriana Kobylak     {
478c79fa915SAdriana Kobylak         auto reply = bus.call(getManagedObjects);
479099543e4SBrad Bishop         reply.read(objects);
480c79fa915SAdriana Kobylak     }
481c79fa915SAdriana Kobylak     catch (const sdbusplus::exception::SdBusError& e)
482c79fa915SAdriana Kobylak     {
483c79fa915SAdriana Kobylak         // Error querying the EntityManager interface. Return the match to have
484c79fa915SAdriana Kobylak         // the callback run if/when the interface appears in D-Bus.
485c79fa915SAdriana Kobylak         return interfacesAddedMatch;
486c79fa915SAdriana Kobylak     }
487099543e4SBrad Bishop 
488099543e4SBrad Bishop     // bind the extension map, host firmware directory, and error callback to
489099543e4SBrad Bishop     // the maybeMakeLinks function.
490099543e4SBrad Bishop     auto maybeMakeLinksWithArgsBound =
491099543e4SBrad Bishop         std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
492099543e4SBrad Bishop                   std::cref(*pHostFirmwareDirectory), std::placeholders::_1,
493099543e4SBrad Bishop                   std::cref(*pErrorCallback));
494099543e4SBrad Bishop 
495099543e4SBrad Bishop     for (const auto& pair : objects)
496099543e4SBrad Bishop     {
497099543e4SBrad Bishop         std::tie(std::ignore, interfacesAndProperties) = pair;
498099543e4SBrad Bishop         // if interfacesAndProperties contains an an instance of
499099543e4SBrad Bishop         // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to see
500099543e4SBrad Bishop         // if links are necessary on this system and if so, create them
501099543e4SBrad Bishop         if (maybeCall(interfacesAndProperties, maybeMakeLinksWithArgsBound))
502099543e4SBrad Bishop         {
503099543e4SBrad Bishop             // The IBMCompatibleSystem interface is already on the bus and the
504099543e4SBrad Bishop             // links were created if applicable.  Instruct the event loop to
505099543e4SBrad Bishop             // exit.
506099543e4SBrad Bishop             loop.exit(0);
507099543e4SBrad Bishop             // The match object isn't needed anymore, so destroy it on return.
508099543e4SBrad Bishop             return nullptr;
509099543e4SBrad Bishop         }
510099543e4SBrad Bishop     }
511099543e4SBrad Bishop 
512099543e4SBrad Bishop     // The IBMCompatibleSystem interface has not yet been published.  Move
513099543e4SBrad Bishop     // ownership of the match callback to the caller.
514099543e4SBrad Bishop     return interfacesAddedMatch;
515099543e4SBrad Bishop }
51653a27395SAdriana Kobylak 
51753a27395SAdriana Kobylak /**
51853a27395SAdriana Kobylak  * @brief Update the Bios Attribute Table
51953a27395SAdriana Kobylak  *
52053a27395SAdriana Kobylak  * If an instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
52153a27395SAdriana Kobylak  * found, update the Bios Attribute Table with the appropriate host firmware
52253a27395SAdriana Kobylak  * data.
52353a27395SAdriana Kobylak  *
52453a27395SAdriana Kobylak  * @param[in] bus - D-Bus client connection.
52553a27395SAdriana Kobylak  * @param[in] extensionMap - Map of IBMCompatibleSystem names and host firmware
52653a27395SAdriana Kobylak  *                           file extensions.
52753a27395SAdriana Kobylak  * @param[in] loop - Program event loop.
52853a27395SAdriana Kobylak  * @return nullptr
52953a27395SAdriana Kobylak  */
53053a27395SAdriana Kobylak std::shared_ptr<void> updateBiosAttrTable(
53153a27395SAdriana Kobylak     sdbusplus::bus::bus& bus,
53253a27395SAdriana Kobylak     std::map<std::string, std::vector<std::string>> extensionMap,
53353a27395SAdriana Kobylak     sdeventplus::Event& loop)
53453a27395SAdriana Kobylak {
53553a27395SAdriana Kobylak     auto pExtensionMap =
53653a27395SAdriana Kobylak         std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
53753a27395SAdriana Kobylak 
53853a27395SAdriana Kobylak     auto getManagedObjects = bus.new_method_call(
53953a27395SAdriana Kobylak         "xyz.openbmc_project.EntityManager", "/",
54053a27395SAdriana Kobylak         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
54153a27395SAdriana Kobylak     std::map<std::string,
54253a27395SAdriana Kobylak              std::map<std::string, std::variant<std::vector<std::string>>>>
54353a27395SAdriana Kobylak         interfacesAndProperties;
54453a27395SAdriana Kobylak     std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)>
54553a27395SAdriana Kobylak         objects;
54653a27395SAdriana Kobylak     try
54753a27395SAdriana Kobylak     {
54853a27395SAdriana Kobylak         auto reply = bus.call(getManagedObjects);
54953a27395SAdriana Kobylak         reply.read(objects);
55053a27395SAdriana Kobylak     }
55153a27395SAdriana Kobylak     catch (const sdbusplus::exception::SdBusError& e)
55253a27395SAdriana Kobylak     {}
55353a27395SAdriana Kobylak 
55453a27395SAdriana Kobylak     auto maybeSetAttrWithArgsBound = std::bind(
55553a27395SAdriana Kobylak         maybeSetBiosAttr, std::cref(*pExtensionMap), std::placeholders::_1);
55653a27395SAdriana Kobylak 
55753a27395SAdriana Kobylak     for (const auto& pair : objects)
55853a27395SAdriana Kobylak     {
55953a27395SAdriana Kobylak         std::tie(std::ignore, interfacesAndProperties) = pair;
56053a27395SAdriana Kobylak         if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound))
56153a27395SAdriana Kobylak         {
56253a27395SAdriana Kobylak             break;
56353a27395SAdriana Kobylak         }
56453a27395SAdriana Kobylak     }
56553a27395SAdriana Kobylak 
56653a27395SAdriana Kobylak     loop.exit(0);
56753a27395SAdriana Kobylak     return nullptr;
56853a27395SAdriana Kobylak }
56953a27395SAdriana Kobylak 
570099543e4SBrad Bishop } // namespace process_hostfirmware
571099543e4SBrad Bishop } // namespace functions
572