blob: d34548918b039f4d18921e9f4552309395db168b [file] [log] [blame]
Brad Bishop099543e2020-11-09 15:37:58 -05001// SPDX-License-Identifier: Apache-2.0
2
3/**@file functions.cpp*/
4
Adriana Kobylak56f538c2021-06-16 20:37:21 +00005#include "config.h"
6
Brad Bishop099543e2020-11-09 15:37:58 -05007#include "functions.hpp"
8
Adriana Kobylakae0998f2021-06-16 19:52:24 +00009#include <nlohmann/json.hpp>
Adriana Kobylak56f538c2021-06-16 20:37:21 +000010#include <phosphor-logging/log.hpp>
Brad Bishop099543e2020-11-09 15:37:58 -050011#include <sdbusplus/bus.hpp>
12#include <sdbusplus/bus/match.hpp>
Adriana Kobylakc79fa912021-06-22 15:37:50 +000013#include <sdbusplus/exception.hpp>
Brad Bishop099543e2020-11-09 15:37:58 -050014#include <sdbusplus/message.hpp>
15#include <sdeventplus/event.hpp>
Adriana Kobylak2b78eb02022-04-06 19:38:47 +000016#include <xyz/openbmc_project/Common/error.hpp>
Brad Bishop099543e2020-11-09 15:37:58 -050017
18#include <filesystem>
Adriana Kobylakae0998f2021-06-16 19:52:24 +000019#include <fstream>
Brad Bishop099543e2020-11-09 15:37:58 -050020#include <functional>
21#include <iostream>
22#include <map>
23#include <memory>
24#include <string>
25#include <variant>
26#include <vector>
27
28namespace functions
29{
30namespace process_hostfirmware
31{
32
Adriana Kobylak56f538c2021-06-16 20:37:21 +000033using namespace phosphor::logging;
Adriana Kobylakebf67bf2021-06-21 18:38:17 +000034using InterfacesPropertiesMap =
35 std::map<std::string,
36 std::map<std::string, std::variant<std::vector<std::string>>>>;
37using ManagedObjectType =
38 std::map<sdbusplus::message::object_path, InterfacesPropertiesMap>;
Adriana Kobylak56f538c2021-06-16 20:37:21 +000039
Brad Bishop099543e2020-11-09 15:37:58 -050040/**
Adriana Kobylakebf67bf2021-06-21 18:38:17 +000041 * @brief Returns the managed objects for a given service
42 */
Patrick Williams0dea1992022-07-22 19:26:52 -050043ManagedObjectType getManagedObjects(sdbusplus::bus_t& bus,
Nan Zhou75352b42022-09-20 20:16:19 +000044 const std::string& service,
45 const std::string& managerPath)
46
Adriana Kobylakebf67bf2021-06-21 18:38:17 +000047{
Nan Zhou75352b42022-09-20 20:16:19 +000048 auto method = bus.new_method_call(service.c_str(), managerPath.c_str(),
Adriana Kobylakebf67bf2021-06-21 18:38:17 +000049 "org.freedesktop.DBus.ObjectManager",
50 "GetManagedObjects");
51
52 ManagedObjectType objects;
53
54 try
55 {
56 auto reply = bus.call(method);
57 reply.read(objects);
58 }
Patrick Williams0dea1992022-07-22 19:26:52 -050059 catch (const sdbusplus::exception_t& e)
Adriana Kobylakebf67bf2021-06-21 18:38:17 +000060 {
61 return ManagedObjectType{};
62 }
63 return objects;
64}
65
66/**
Brad Bishop099543e2020-11-09 15:37:58 -050067 * @brief Issue callbacks safely
68 *
69 * std::function can be empty, so this wrapper method checks for that prior to
70 * calling it to avoid std::bad_function_call
71 *
72 * @tparam Sig the types of the std::function arguments
73 * @tparam Args the deduced argument types
74 * @param[in] callback the callback being wrapped
75 * @param[in] args the callback arguments
76 */
77template <typename... Sig, typename... Args>
78void makeCallback(const std::function<void(Sig...)>& callback, Args&&... args)
79{
80 if (callback)
81 {
82 callback(std::forward<Args>(args)...);
83 }
84}
85
86/**
Adriana Kobylak749bdcc2024-01-17 12:56:00 -060087 * @brief Get file extensions for Compatible
Brad Bishop099543e2020-11-09 15:37:58 -050088 *
Adriana Kobylak749bdcc2024-01-17 12:56:00 -060089 * IBM host firmware can be deployed as blobs (files) in a filesystem. Host
Brad Bishop099543e2020-11-09 15:37:58 -050090 * firmware blobs for different values of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -060091 * xyz.openbmc_project.Inventory.Decorator.Compatible are packaged with
92 * different filename extensions. getExtensionsForIbmCompatibleSystem maintains
93 * the mapping from a given value of
94 * xyz.openbmc_project.Inventory.Decorator.Compatible to an array of filename
95 * extensions.
Brad Bishop099543e2020-11-09 15:37:58 -050096 *
97 * If a mapping is found getExtensionsForIbmCompatibleSystem returns true and
Adriana Kobylak749bdcc2024-01-17 12:56:00 -060098 * the extensions parameter is reset with the map entry. If no mapping is found
99 * getExtensionsForIbmCompatibleSystem returns false and extensions is
Brad Bishop099543e2020-11-09 15:37:58 -0500100 * unmodified.
101 *
102 * @param[in] extensionMap a map of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600103 * xyz.openbmc_project.Inventory.Decorator.Compatible to host firmware blob file
104 * extensions.
Brad Bishop099543e2020-11-09 15:37:58 -0500105 * @param[in] ibmCompatibleSystem The names property of an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600106 * xyz.openbmc_project.Inventory.Decorator.Compatible
Manojkiran Eda96442c82024-06-17 10:24:05 +0530107 * @param[out] extensions the host firmware blob file extensions
Brad Bishop099543e2020-11-09 15:37:58 -0500108 * @return true if an entry was found, otherwise false
109 */
110bool getExtensionsForIbmCompatibleSystem(
111 const std::map<std::string, std::vector<std::string>>& extensionMap,
112 const std::vector<std::string>& ibmCompatibleSystem,
113 std::vector<std::string>& extensions)
114{
115 for (const auto& system : ibmCompatibleSystem)
116 {
117 auto extensionMapIterator = extensionMap.find(system);
118 if (extensionMapIterator != extensionMap.end())
119 {
120 extensions = extensionMapIterator->second;
121 return true;
122 }
123 }
124
125 return false;
126}
127
128/**
129 * @brief Write host firmware well-known name
130 *
131 * A wrapper around std::filesystem::create_symlink that avoids EEXIST by
132 * deleting any pre-existing file.
133 *
134 * @param[in] linkTarget The link target argument to
135 * std::filesystem::create_symlink
136 * @param[in] linkPath The link path argument to std::filesystem::create_symlink
137 * @param[in] errorCallback A callback made in the event of filesystem errors.
138 */
139void writeLink(const std::filesystem::path& linkTarget,
140 const std::filesystem::path& linkPath,
141 const ErrorCallbackType& errorCallback)
142{
143 std::error_code ec;
144
145 // remove files with the same name as the symlink to be created,
146 // otherwise symlink will fail with EEXIST.
147 if (!std::filesystem::remove(linkPath, ec))
148 {
149 if (ec)
150 {
151 makeCallback(errorCallback, linkPath, ec);
152 return;
153 }
154 }
155
156 std::filesystem::create_symlink(linkTarget, linkPath, ec);
157 if (ec)
158 {
159 makeCallback(errorCallback, linkPath, ec);
160 return;
161 }
162}
163
164/**
165 * @brief Find host firmware blob files that need well-known names
166 *
167 * The IBM host firmware runtime looks for data and/or additional code while
Manojkiran Eda96442c82024-06-17 10:24:05 +0530168 * bootstrapping in files with well-known names. findLinks uses the provided
Brad Bishop099543e2020-11-09 15:37:58 -0500169 * extensions argument to find host firmware blob files that require a
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600170 * well-known name. When a blob is found, issue the provided callback
Brad Bishop099543e2020-11-09 15:37:58 -0500171 * (typically a function that will write a symlink).
172 *
173 * @param[in] hostFirmwareDirectory The directory in which findLinks should
174 * look for host firmware blob files that need well-known names.
Manojkiran Eda96442c82024-06-17 10:24:05 +0530175 * @param[in] extensions The extensions of the firmware blob files denote a
Brad Bishop099543e2020-11-09 15:37:58 -0500176 * host firmware blob file requires a well-known name.
177 * @param[in] errorCallback A callback made in the event of filesystem errors.
178 * @param[in] linkCallback A callback made when host firmware blob files
179 * needing a well known name are found.
180 */
181void findLinks(const std::filesystem::path& hostFirmwareDirectory,
182 const std::vector<std::string>& extensions,
183 const ErrorCallbackType& errorCallback,
184 const LinkCallbackType& linkCallback)
185{
186 std::error_code ec;
187 std::filesystem::directory_iterator directoryIterator(hostFirmwareDirectory,
188 ec);
189 if (ec)
190 {
191 makeCallback(errorCallback, hostFirmwareDirectory, ec);
192 return;
193 }
194
Adriana Kobylak4e82bc82021-09-28 19:25:39 +0000195 // Create a symlink for pnor.toc
196 static const auto tocLid = "81e00994.lid";
197 auto tocLidPath = hostFirmwareDirectory / tocLid;
198 if (std::filesystem::exists(tocLidPath))
199 {
200 static const auto tocName = "pnor.toc";
201 auto tocLinkPath = hostFirmwareDirectory / tocName;
202 makeCallback(linkCallback, tocLid, tocLinkPath, errorCallback);
203 }
204
Brad Bishop099543e2020-11-09 15:37:58 -0500205 for (; directoryIterator != std::filesystem::end(directoryIterator);
206 directoryIterator.increment(ec))
207 {
208 const auto& file = directoryIterator->path();
209 if (ec)
210 {
211 makeCallback(errorCallback, file, ec);
212 // quit here if the increment call failed otherwise the loop may
213 // never finish
214 break;
215 }
216
217 if (std::find(extensions.begin(), extensions.end(), file.extension()) ==
218 extensions.end())
219 {
220 // this file doesn't have an extension or doesn't match any of the
221 // provided extensions.
222 continue;
223 }
224
225 auto linkPath(file.parent_path().append(
226 static_cast<const std::string&>(file.stem())));
227
228 makeCallback(linkCallback, file.filename(), linkPath, errorCallback);
229 }
230}
231
232/**
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000233 * @brief Parse the elements json file and construct a string with the data to
234 * be used to update the bios attribute table.
235 *
236 * @param[in] elementsJsonFilePath - The path to the host firmware json file.
237 * @param[in] extensions - The extensions of the firmware blob files.
Adriana Kobylak53a27392021-06-14 17:42:40 +0000238 */
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000239std::string getBiosAttrStr(const std::filesystem::path& elementsJsonFilePath,
240 const std::vector<std::string>& extensions)
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000241{
242 std::string biosAttrStr{};
243
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000244 std::ifstream jsonFile(elementsJsonFilePath.c_str());
245 if (!jsonFile)
246 {
247 return {};
248 }
249
250 std::map<std::string, std::string> attr;
251 auto data = nlohmann::json::parse(jsonFile, nullptr, false);
252 if (data.is_discarded())
253 {
254 log<level::ERR>("Error parsing JSON file",
255 entry("FILE=%s", elementsJsonFilePath.c_str()));
256 return {};
257 }
258
259 // .get requires a non-const iterator
260 for (auto& iter : data["lids"])
261 {
262 std::string name{};
263 std::string lid{};
264
265 try
266 {
267 name = iter["element_name"].get<std::string>();
268 lid = iter["short_lid_name"].get<std::string>();
269 }
Patrick Williams97a709b2021-10-06 13:05:40 -0500270 catch (const std::exception& e)
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000271 {
272 // Possibly the element or lid name field was not found
273 log<level::ERR>("Error reading JSON field",
274 entry("FILE=%s", elementsJsonFilePath.c_str()),
275 entry("ERROR=%s", e.what()));
276 continue;
277 }
278
279 // The elements with the ipl extension have higher priority. Therefore
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000280 // Use operator[] to overwrite value if an entry for it already exists,
281 // and create a second entry with key name element_RT to specify it as
282 // a runtime element.
283 // Ex: if the JSON contains an entry A.P10 with lid name X, it'll create
284 // and try A=X. If the JSON also contained an entry A.P10.iplTime with
285 // lid name Y, the A entry would be overwritten to be A=Y and a second
286 // entry A_RT=X would be created.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000287 constexpr auto iplExtension = ".iplTime";
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000288 constexpr auto runtimeSuffix = "_RT";
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000289 std::filesystem::path path(name);
290 if (path.extension() == iplExtension)
291 {
292 // Some elements have an additional extension, ex: .P10.iplTime
293 // Strip off the ipl extension with stem(), then check if there is
294 // an additional extension with extension().
295 if (!path.stem().extension().empty())
296 {
297 // Check if the extension matches the extensions for this system
298 if (std::find(extensions.begin(), extensions.end(),
299 path.stem().extension()) == extensions.end())
300 {
301 continue;
302 }
303 }
304 // Get the element name without extensions by calling stem() twice
305 // since stem() returns the base name if no periods are found.
306 // Therefore both "element.P10" and "element.P10.iplTime" would
307 // become "element".
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000308 auto keyName = path.stem().stem();
309 auto attrIt = attr.find(keyName);
310 if (attrIt != attr.end())
311 {
312 // Copy the existing entry to a runtime entry
313 auto runtimeKeyName = keyName.string() + runtimeSuffix;
314 attr.insert({runtimeKeyName, attrIt->second});
315 }
Manojkiran Eda96442c82024-06-17 10:24:05 +0530316 // Overwrite the existing element with the ipl entry
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000317 attr[keyName] = lid;
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000318 continue;
319 }
320
321 // Process all other extensions. The extension should match the list of
322 // supported extensions for this system. Use .insert() to only add
323 // entries that do not exist, so to not overwrite the values that may
324 // had been added that had the ipl extension.
325 if (std::find(extensions.begin(), extensions.end(), path.extension()) !=
326 extensions.end())
327 {
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000328 auto keyName = path.stem();
329 auto attrIt = attr.find(keyName);
330 if (attrIt != attr.end())
331 {
332 // The existing entry is an ipl entry, therefore create this
333 // entry as a runtime one.
334 auto runtimeKeyName = keyName.string() + runtimeSuffix;
335 attr.insert({runtimeKeyName, lid});
336 }
337 else
338 {
339 attr.insert({path.stem(), lid});
340 }
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000341 }
342 }
343 for (const auto& a : attr)
344 {
345 // Build the bios attribute string with format:
346 // "element1=lid1,element2=lid2,elementN=lidN,"
347 biosAttrStr += a.first + "=" + a.second + ",";
Adriana Kobylak5dc5d6c2021-07-20 20:21:16 +0000348
349 // Create symlinks from the hostfw elements to their corresponding
350 // lid files if they don't exist
Patrick Williams7fb6c342023-05-10 07:50:18 -0500351 auto elementFilePath = std::filesystem::path("/media/hostfw/running") /
352 a.first;
Adriana Kobylak5dc5d6c2021-07-20 20:21:16 +0000353 if (!std::filesystem::exists(elementFilePath))
354 {
355 std::error_code ec;
356 auto lidName = a.second + ".lid";
357 std::filesystem::create_symlink(lidName, elementFilePath, ec);
358 if (ec)
359 {
360 log<level::ERR>("Error creating symlink",
361 entry("TARGET=%s", lidName.c_str()),
362 entry("LINK=%s", elementFilePath.c_str()));
363 }
364 }
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000365 }
366
George Liua38f6e62021-09-20 08:53:00 +0800367 // Delete the last comma of the bios attribute string
368 if (biosAttrStr.back() == ',')
369 {
370 return biosAttrStr.substr(0, biosAttrStr.length() - 1);
371 }
372
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000373 return biosAttrStr;
374}
375
376/**
377 * @brief Set the bios attribute table with details of the host firmware data
378 * for this system.
379 *
380 * @param[in] elementsJsonFilePath - The path to the host firmware json file.
Manojkiran Eda96442c82024-06-17 10:24:05 +0530381 * @param[in] extensions - The extensions of the firmware blob files.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000382 */
383void setBiosAttr(const std::filesystem::path& elementsJsonFilePath,
384 const std::vector<std::string>& extensions)
385{
386 auto biosAttrStr = getBiosAttrStr(elementsJsonFilePath, extensions);
387
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000388 constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager";
389 constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager";
390 constexpr auto dbusAttrName = "hb_lid_ids";
391 constexpr auto dbusAttrType =
392 "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
393
394 using PendingAttributesType = std::vector<std::pair<
395 std::string, std::tuple<std::string, std::variant<std::string>>>>;
396 PendingAttributesType pendingAttributes;
397 pendingAttributes.emplace_back(std::make_pair(
398 dbusAttrName, std::make_tuple(dbusAttrType, biosAttrStr)));
399
400 auto bus = sdbusplus::bus::new_default();
401 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
402 MAPPER_INTERFACE, "GetObject");
403 method.append(biosConfigPath, std::vector<std::string>({biosConfigIntf}));
404 std::vector<std::pair<std::string, std::vector<std::string>>> response;
405 try
406 {
407 auto reply = bus.call(method);
408 reply.read(response);
409 if (response.empty())
410 {
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000411 log<level::INFO>("Error reading mapper response",
412 entry("PATH=%s", biosConfigPath),
413 entry("INTERFACE=%s", biosConfigIntf));
414 throw sdbusplus::xyz::openbmc_project::Common::Error::
415 InternalFailure();
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000416 }
417 auto method = bus.new_method_call((response.begin()->first).c_str(),
418 biosConfigPath,
419 SYSTEMD_PROPERTY_INTERFACE, "Set");
420 method.append(biosConfigIntf, "PendingAttributes",
421 std::variant<PendingAttributesType>(pendingAttributes));
422 bus.call(method);
423 }
Patrick Williams0dea1992022-07-22 19:26:52 -0500424 catch (const sdbusplus::exception_t& e)
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000425 {
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000426 log<level::INFO>("Error setting the bios attribute",
427 entry("ERROR=%s", e.what()),
428 entry("ATTRIBUTE=%s", dbusAttrName));
429 throw;
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000430 }
431}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000432
433/**
Brad Bishop099543e2020-11-09 15:37:58 -0500434 * @brief Make callbacks on
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600435 * xyz.openbmc_project.Inventory.Decorator.Compatible instances.
Brad Bishop099543e2020-11-09 15:37:58 -0500436 *
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600437 * Look for an instance of xyz.openbmc_project.Inventory.Decorator.Compatible in
438 * the provided argument and if found, issue the provided callback.
Brad Bishop099543e2020-11-09 15:37:58 -0500439 *
440 * @param[in] interfacesAndProperties the interfaces in which to look for an
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600441 * instance of xyz.openbmc_project.Inventory.Decorator.Compatible
Brad Bishop099543e2020-11-09 15:37:58 -0500442 * @param[in] callback the user callback to make if
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600443 * xyz.openbmc_project.Inventory.Decorator.Compatible is found in
Brad Bishop099543e2020-11-09 15:37:58 -0500444 * interfacesAndProperties
445 * @return true if interfacesAndProperties contained an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600446 * xyz.openbmc_project.Inventory.Decorator.Compatible, false otherwise
Brad Bishop099543e2020-11-09 15:37:58 -0500447 */
448bool maybeCall(const std::map<std::string,
449 std::map<std::string,
450 std::variant<std::vector<std::string>>>>&
451 interfacesAndProperties,
452 const MaybeCallCallbackType& callback)
453{
454 using namespace std::string_literals;
455
456 static const auto interfaceName =
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600457 "xyz.openbmc_project.Inventory.Decorator.Compatible"s;
Brad Bishop099543e2020-11-09 15:37:58 -0500458 auto interfaceIterator = interfacesAndProperties.find(interfaceName);
459 if (interfaceIterator == interfacesAndProperties.cend())
460 {
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600461 // Compatible interface not found, so instruct the caller to keep
462 // waiting or try again later.
Brad Bishop099543e2020-11-09 15:37:58 -0500463 return false;
464 }
465 auto propertyIterator = interfaceIterator->second.find("Names"s);
466 if (propertyIterator == interfaceIterator->second.cend())
467 {
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600468 // The interface exists but the property doesn't. This is a bug in the
469 // Compatible implementation. The caller should not try again.
Brad Bishop099543e2020-11-09 15:37:58 -0500470 std::cerr << "Names property not implemented on " << interfaceName
471 << "\n";
472 return true;
473 }
474
475 const auto& ibmCompatibleSystem =
476 std::get<std::vector<std::string>>(propertyIterator->second);
477 if (callback)
478 {
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000479 try
480 {
481 callback(ibmCompatibleSystem);
482 }
Patrick Williams0dea1992022-07-22 19:26:52 -0500483 catch (const sdbusplus::exception_t& e)
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000484 {
485 return false;
486 }
Brad Bishop099543e2020-11-09 15:37:58 -0500487 }
488
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600489 // Compatible found and callback issued.
Brad Bishop099543e2020-11-09 15:37:58 -0500490 return true;
491}
492
493/**
494 * @brief Make callbacks on
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600495 * xyz.openbmc_project.Inventory.Decorator.Compatible instances.
Brad Bishop099543e2020-11-09 15:37:58 -0500496 *
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600497 * Look for an instance ofxyz.openbmc_project.Inventory.Decorator.Compatible in
498 * the provided argument and if found, issue the provided callback.
Brad Bishop099543e2020-11-09 15:37:58 -0500499 *
500 * @param[in] message the DBus message in which to look for an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600501 * xyz.openbmc_project.Inventory.Decorator.Compatible
Brad Bishop099543e2020-11-09 15:37:58 -0500502 * @param[in] callback the user callback to make if
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600503 * xyz.openbmc_project.Inventory.Decorator.Compatible is found in message
Brad Bishop099543e2020-11-09 15:37:58 -0500504 * @return true if message contained an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600505 * xyz.openbmc_project.Inventory.Decorator.Compatible, false otherwise
Brad Bishop099543e2020-11-09 15:37:58 -0500506 */
Patrick Williams0dea1992022-07-22 19:26:52 -0500507bool maybeCallMessage(sdbusplus::message_t& message,
Brad Bishop099543e2020-11-09 15:37:58 -0500508 const MaybeCallCallbackType& callback)
509{
510 std::map<std::string,
511 std::map<std::string, std::variant<std::vector<std::string>>>>
512 interfacesAndProperties;
513 sdbusplus::message::object_path _;
514 message.read(_, interfacesAndProperties);
515 return maybeCall(interfacesAndProperties, callback);
516}
517
518/**
519 * @brief Determine system support for host firmware well-known names.
520 *
521 * Using the provided extensionMap and
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600522 * xyz.openbmc_project.Inventory.Decorator.Compatible, determine if well-known
Manojkiran Eda96442c82024-06-17 10:24:05 +0530523 * names for host firmware blob files are necessary and if so, create them.
Brad Bishop099543e2020-11-09 15:37:58 -0500524 *
525 * @param[in] extensionMap a map of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600526 * xyz.openbmc_project.Inventory.Decorator.Compatible to host firmware blob file
527 * extensions.
528 * @param[in] hostFirmwareDirectory The directory in which findLinks should look
529 * for host firmware blob files that need well-known names.
Brad Bishop099543e2020-11-09 15:37:58 -0500530 * @param[in] ibmCompatibleSystem The names property of an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600531 * xyz.openbmc_project.Inventory.Decorator.Compatible
Brad Bishop099543e2020-11-09 15:37:58 -0500532 * @param[in] errorCallback A callback made in the event of filesystem errors.
533 */
534void maybeMakeLinks(
535 const std::map<std::string, std::vector<std::string>>& extensionMap,
536 const std::filesystem::path& hostFirmwareDirectory,
537 const std::vector<std::string>& ibmCompatibleSystem,
538 const ErrorCallbackType& errorCallback)
539{
540 std::vector<std::string> extensions;
541 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
542 extensions))
543 {
544 findLinks(hostFirmwareDirectory, extensions, errorCallback, writeLink);
545 }
546}
547
548/**
Adriana Kobylak53a27392021-06-14 17:42:40 +0000549 * @brief Determine system support for updating the bios attribute table.
550 *
551 * Using the provided extensionMap and
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600552 * xyz.openbmc_project.Inventory.Decorator.Compatible, determine if the bios
Adriana Kobylak53a27392021-06-14 17:42:40 +0000553 * attribute table needs to be updated.
554 *
555 * @param[in] extensionMap a map of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600556 * xyz.openbmc_project.Inventory.Decorator.Compatible to host firmware blob file
557 * extensions.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000558 * @param[in] elementsJsonFilePath The file path to the json file
Adriana Kobylak53a27392021-06-14 17:42:40 +0000559 * @param[in] ibmCompatibleSystem The names property of an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600560 * xyz.openbmc_project.Inventory.Decorator.Compatible
Adriana Kobylak53a27392021-06-14 17:42:40 +0000561 */
562void maybeSetBiosAttr(
563 const std::map<std::string, std::vector<std::string>>& extensionMap,
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000564 const std::filesystem::path& elementsJsonFilePath,
Adriana Kobylak53a27392021-06-14 17:42:40 +0000565 const std::vector<std::string>& ibmCompatibleSystem)
566{
567 std::vector<std::string> extensions;
568 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
569 extensions))
570 {
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000571 try
572 {
573 setBiosAttr(elementsJsonFilePath, extensions);
574 }
Patrick Williams0dea1992022-07-22 19:26:52 -0500575 catch (const sdbusplus::exception_t& e)
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000576 {
577 throw;
578 }
Adriana Kobylak53a27392021-06-14 17:42:40 +0000579 }
580}
581
582/**
Brad Bishop099543e2020-11-09 15:37:58 -0500583 * @brief process host firmware
584 *
585 * Allocate a callback context and register for DBus.ObjectManager Interfaces
586 * added signals from entity manager.
587 *
588 * Check the current entity manager object tree for a
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600589 * xyz.openbmc_project.Inventory.Decorator.Compatible instance (entity manager
590 * will be dbus activated if it is not running). If one is found, determine if
591 * symlinks need to be created and create them. Instruct the program event loop
592 * to exit.
Brad Bishop099543e2020-11-09 15:37:58 -0500593 *
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600594 * If no instance of xyz.openbmc_project.Inventory.Decorator.Compatible is found
595 * return the callback context to main, where the program will sleep until the
596 * callback is invoked one or more times and instructs the program event loop to
597 * exit when xyz.openbmc_project.Inventory.Decorator.Compatible is added.
Brad Bishop099543e2020-11-09 15:37:58 -0500598 *
599 * @param[in] bus a DBus client connection
600 * @param[in] extensionMap a map of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600601 * xyz.openbmc_project.Inventory.Decorator.Compatible to host firmware blob file
602 * extensions.
Brad Bishop099543e2020-11-09 15:37:58 -0500603 * @param[in] hostFirmwareDirectory The directory in which processHostFirmware
604 * should look for blob files.
605 * @param[in] errorCallback A callback made in the event of filesystem errors.
606 * @param[in] loop a program event loop
607 * @return nullptr if an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600608 * xyz.openbmc_project.Inventory.Decorator.Compatible is found, otherwise a
Brad Bishop099543e2020-11-09 15:37:58 -0500609 * pointer to an sdbusplus match object.
610 */
611std::shared_ptr<void> processHostFirmware(
Patrick Williams0dea1992022-07-22 19:26:52 -0500612 sdbusplus::bus_t& bus,
Brad Bishop099543e2020-11-09 15:37:58 -0500613 std::map<std::string, std::vector<std::string>> extensionMap,
614 std::filesystem::path hostFirmwareDirectory,
615 ErrorCallbackType errorCallback, sdeventplus::Event& loop)
616{
617 // ownership of extensionMap, hostFirmwareDirectory and errorCallback can't
Manojkiran Eda96442c82024-06-17 10:24:05 +0530618 // be transferred to the match callback because they are needed in the non
Brad Bishop099543e2020-11-09 15:37:58 -0500619 // async part of this function below, so they need to be moved to the heap.
620 auto pExtensionMap =
621 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
622 auto pHostFirmwareDirectory =
623 std::make_shared<decltype(hostFirmwareDirectory)>(
624 std::move(hostFirmwareDirectory));
625 auto pErrorCallback =
626 std::make_shared<decltype(errorCallback)>(std::move(errorCallback));
627
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600628 // register for a callback in case the Compatible interface has not yet been
629 // published by entity manager.
Patrick Williams0dea1992022-07-22 19:26:52 -0500630 auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match_t>(
Brad Bishop099543e2020-11-09 15:37:58 -0500631 bus,
632 sdbusplus::bus::match::rules::interfacesAdded() +
633 sdbusplus::bus::match::rules::sender(
634 "xyz.openbmc_project.EntityManager"),
635 [pExtensionMap, pHostFirmwareDirectory, pErrorCallback,
636 &loop](auto& message) {
Patrick Williams7fb6c342023-05-10 07:50:18 -0500637 // bind the extension map, host firmware directory, and error
638 // callback to the maybeMakeLinks function.
639 auto maybeMakeLinksWithArgsBound =
640 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
641 std::cref(*pHostFirmwareDirectory), std::placeholders::_1,
642 std::cref(*pErrorCallback));
Brad Bishop099543e2020-11-09 15:37:58 -0500643
Patrick Williams7fb6c342023-05-10 07:50:18 -0500644 // if the InterfacesAdded message contains an an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600645 // xyz.openbmc_project.Inventory.Decorator.Compatible, check to see if
646 // links are necessary on this system and if so, create them.
Patrick Williams7fb6c342023-05-10 07:50:18 -0500647 if (maybeCallMessage(message, maybeMakeLinksWithArgsBound))
648 {
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600649 // The Compatible interface was found and the links were created if
650 // applicable. Instruct the event loop / subcommand to exit.
Patrick Williams7fb6c342023-05-10 07:50:18 -0500651 loop.exit(0);
652 }
Patrick Williamsaf29f722023-10-20 11:18:27 -0500653 });
Brad Bishop099543e2020-11-09 15:37:58 -0500654
655 // now that we'll get a callback in the event of an InterfacesAdded signal
656 // (potentially containing
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600657 // xyz.openbmc_project.Inventory.Decorator.Compatible), activate entity
Brad Bishop099543e2020-11-09 15:37:58 -0500658 // manager if it isn't running and enumerate its objects
659 auto getManagedObjects = bus.new_method_call(
Nan Zhou75352b42022-09-20 20:16:19 +0000660 "xyz.openbmc_project.EntityManager", "/xyz/openbmc_project/inventory",
Brad Bishop099543e2020-11-09 15:37:58 -0500661 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Brad Bishop099543e2020-11-09 15:37:58 -0500662 std::map<std::string,
663 std::map<std::string, std::variant<std::vector<std::string>>>>
664 interfacesAndProperties;
665 std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)>
666 objects;
Adriana Kobylakc79fa912021-06-22 15:37:50 +0000667 try
668 {
669 auto reply = bus.call(getManagedObjects);
670 reply.read(objects);
671 }
Patrick Williams0dea1992022-07-22 19:26:52 -0500672 catch (const sdbusplus::exception_t& e)
Adriana Kobylakc79fa912021-06-22 15:37:50 +0000673 {
674 // Error querying the EntityManager interface. Return the match to have
675 // the callback run if/when the interface appears in D-Bus.
676 return interfacesAddedMatch;
677 }
Brad Bishop099543e2020-11-09 15:37:58 -0500678
679 // bind the extension map, host firmware directory, and error callback to
680 // the maybeMakeLinks function.
681 auto maybeMakeLinksWithArgsBound =
682 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
683 std::cref(*pHostFirmwareDirectory), std::placeholders::_1,
684 std::cref(*pErrorCallback));
685
686 for (const auto& pair : objects)
687 {
688 std::tie(std::ignore, interfacesAndProperties) = pair;
689 // if interfacesAndProperties contains an an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600690 // xyz.openbmc_project.Inventory.Decorator.Compatible, check to see if
691 // links are necessary on this system and if so, create them
Brad Bishop099543e2020-11-09 15:37:58 -0500692 if (maybeCall(interfacesAndProperties, maybeMakeLinksWithArgsBound))
693 {
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600694 // The Compatible interface is already on the bus and the links were
695 // created if applicable. Instruct the event loop to exit.
Brad Bishop099543e2020-11-09 15:37:58 -0500696 loop.exit(0);
697 // The match object isn't needed anymore, so destroy it on return.
698 return nullptr;
699 }
700 }
701
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600702 // The Compatible interface has not yet been published. Move ownership of
703 // the match callback to the caller.
Brad Bishop099543e2020-11-09 15:37:58 -0500704 return interfacesAddedMatch;
705}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000706
707/**
708 * @brief Update the Bios Attribute Table
709 *
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600710 * If an instance of xyz.openbmc_project.Inventory.Decorator.Compatible is
Adriana Kobylak53a27392021-06-14 17:42:40 +0000711 * found, update the Bios Attribute Table with the appropriate host firmware
712 * data.
713 *
714 * @param[in] bus - D-Bus client connection.
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600715 * @param[in] extensionMap - Map of Compatible names and host firmware file
716 extensions.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000717 * @param[in] elementsJsonFilePath - The Path to the json file
Adriana Kobylak53a27392021-06-14 17:42:40 +0000718 * @param[in] loop - Program event loop.
719 * @return nullptr
720 */
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000721std::vector<std::shared_ptr<void>> updateBiosAttrTable(
Patrick Williams0dea1992022-07-22 19:26:52 -0500722 sdbusplus::bus_t& bus,
Adriana Kobylak53a27392021-06-14 17:42:40 +0000723 std::map<std::string, std::vector<std::string>> extensionMap,
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000724 std::filesystem::path elementsJsonFilePath, sdeventplus::Event& loop)
Adriana Kobylak53a27392021-06-14 17:42:40 +0000725{
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000726 constexpr auto pldmPath = "/xyz/openbmc_project/pldm";
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000727 constexpr auto entityManagerServiceName =
728 "xyz.openbmc_project.EntityManager";
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000729
Adriana Kobylak53a27392021-06-14 17:42:40 +0000730 auto pExtensionMap =
731 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000732 auto pElementsJsonFilePath =
733 std::make_shared<decltype(elementsJsonFilePath)>(
734 std::move(elementsJsonFilePath));
Adriana Kobylak53a27392021-06-14 17:42:40 +0000735
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000736 auto maybeSetAttrWithArgsBound =
737 std::bind(maybeSetBiosAttr, std::cref(*pExtensionMap),
738 std::cref(*pElementsJsonFilePath), std::placeholders::_1);
739
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000740 std::vector<std::shared_ptr<void>> matches;
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000741
742 // Entity Manager is needed to get the list of supported extensions. Add a
743 // match to monitor interfaces added in case it's not running yet.
Patrick Williams0dea1992022-07-22 19:26:52 -0500744 matches.emplace_back(std::make_shared<sdbusplus::bus::match_t>(
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000745 bus,
746 sdbusplus::bus::match::rules::interfacesAdded() +
747 sdbusplus::bus::match::rules::sender(
748 "xyz.openbmc_project.EntityManager"),
749 [pldmPath, pExtensionMap, pElementsJsonFilePath,
750 maybeSetAttrWithArgsBound, &loop](auto& message) {
Patrick Williams7fb6c342023-05-10 07:50:18 -0500751 if (maybeCallMessage(message, maybeSetAttrWithArgsBound))
752 {
753 loop.exit(0);
754 }
Patrick Williamsaf29f722023-10-20 11:18:27 -0500755 }));
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000756
757 // The BIOS attribute table can only be updated if PLDM is running because
758 // PLDM is the one that exposes this property. Add a match to monitor when
759 // the PLDM service starts.
Patrick Williams0dea1992022-07-22 19:26:52 -0500760 matches.emplace_back(std::make_shared<sdbusplus::bus::match_t>(
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000761 bus,
762 sdbusplus::bus::match::rules::nameOwnerChanged() +
763 sdbusplus::bus::match::rules::arg0namespace(
764 "xyz.openbmc_project.PLDM"),
765 [pExtensionMap, pElementsJsonFilePath, maybeSetAttrWithArgsBound,
766 &loop](auto& message) {
Patrick Williams7fb6c342023-05-10 07:50:18 -0500767 std::string name;
768 std::string oldOwner;
769 std::string newOwner;
770 message.read(name, oldOwner, newOwner);
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000771
Patrick Williams7fb6c342023-05-10 07:50:18 -0500772 if (newOwner.empty())
773 {
774 return;
775 }
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000776
Patrick Williams7fb6c342023-05-10 07:50:18 -0500777 auto bus = sdbusplus::bus::new_default();
778 InterfacesPropertiesMap interfacesAndProperties;
779 auto objects = getManagedObjects(bus, entityManagerServiceName,
780 "/xyz/openbmc_project/inventory");
781 for (const auto& pair : objects)
782 {
783 std::tie(std::ignore, interfacesAndProperties) = pair;
784 if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound))
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000785 {
Patrick Williams7fb6c342023-05-10 07:50:18 -0500786 loop.exit(0);
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000787 }
Patrick Williams7fb6c342023-05-10 07:50:18 -0500788 }
Patrick Williamsaf29f722023-10-20 11:18:27 -0500789 }));
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000790
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000791 InterfacesPropertiesMap interfacesAndProperties;
Nan Zhou75352b42022-09-20 20:16:19 +0000792 auto objects = getManagedObjects(bus, entityManagerServiceName,
793 "/xyz/openbmc_project/inventory");
Adriana Kobylak53a27392021-06-14 17:42:40 +0000794 for (const auto& pair : objects)
795 {
796 std::tie(std::ignore, interfacesAndProperties) = pair;
797 if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound))
798 {
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000799 loop.exit(0);
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000800 return {};
Adriana Kobylak53a27392021-06-14 17:42:40 +0000801 }
802 }
803
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000804 return matches;
Adriana Kobylak53a27392021-06-14 17:42:40 +0000805}
806
Brad Bishop099543e2020-11-09 15:37:58 -0500807} // namespace process_hostfirmware
808} // namespace functions