blob: 373dcc1592935bd3e894947fcfb9f767ccfccd91 [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>
16
17#include <filesystem>
Adriana Kobylakae0998f2021-06-16 19:52:24 +000018#include <fstream>
Brad Bishop099543e2020-11-09 15:37:58 -050019#include <functional>
20#include <iostream>
21#include <map>
22#include <memory>
23#include <string>
24#include <variant>
25#include <vector>
26
27namespace functions
28{
29namespace process_hostfirmware
30{
31
Adriana Kobylak56f538c2021-06-16 20:37:21 +000032using namespace phosphor::logging;
33
Brad Bishop099543e2020-11-09 15:37:58 -050034/**
Adriana Kobylakfd4a6082021-06-21 15:53:34 +000035 * @brief GetObject function to find the service given an object path.
36 * It is used to determine if a service is running, so there is no need
37 * to specify interfaces as a parameter to constrain the search.
38 */
39std::string getObject(sdbusplus::bus::bus& bus, const std::string& path)
40{
41 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
42 MAPPER_BUSNAME, "GetObject");
43 method.append(path);
44 std::vector<std::string> interfaces;
45 method.append(interfaces);
46
47 std::vector<std::pair<std::string, std::vector<std::string>>> response;
48
49 try
50 {
51 auto reply = bus.call(method);
52 reply.read(response);
53 if (response.empty())
54 {
55 return std::string{};
56 }
57 }
58 catch (const sdbusplus::exception::SdBusError& e)
59 {
60 return std::string{};
61 }
62 return response[0].first;
63}
64
65/**
Brad Bishop099543e2020-11-09 15:37:58 -050066 * @brief Issue callbacks safely
67 *
68 * std::function can be empty, so this wrapper method checks for that prior to
69 * calling it to avoid std::bad_function_call
70 *
71 * @tparam Sig the types of the std::function arguments
72 * @tparam Args the deduced argument types
73 * @param[in] callback the callback being wrapped
74 * @param[in] args the callback arguments
75 */
76template <typename... Sig, typename... Args>
77void makeCallback(const std::function<void(Sig...)>& callback, Args&&... args)
78{
79 if (callback)
80 {
81 callback(std::forward<Args>(args)...);
82 }
83}
84
85/**
86 * @brief Get file extensions for IBMCompatibleSystem
87 *
88 * IBM host firmware can be deployed as blobs (files) in a filesystem. Host
89 * firmware blobs for different values of
90 * xyz.openbmc_project.Configuration.IBMCompatibleSystem are packaged with
91 * different filename extensions. getExtensionsForIbmCompatibleSystem
92 * maintains the mapping from a given value of
93 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to an array of
94 * filename extensions.
95 *
96 * If a mapping is found getExtensionsForIbmCompatibleSystem returns true and
97 * the extensions parameter is reset with the map entry. If no mapping is
98 * found getExtensionsForIbmCompatibleSystem returns false and extensions is
99 * unmodified.
100 *
101 * @param[in] extensionMap a map of
102 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
103 * file extensions.
104 * @param[in] ibmCompatibleSystem The names property of an instance of
105 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
106 * @param[out] extentions the host firmware blob file extensions
107 * @return true if an entry was found, otherwise false
108 */
109bool getExtensionsForIbmCompatibleSystem(
110 const std::map<std::string, std::vector<std::string>>& extensionMap,
111 const std::vector<std::string>& ibmCompatibleSystem,
112 std::vector<std::string>& extensions)
113{
114 for (const auto& system : ibmCompatibleSystem)
115 {
116 auto extensionMapIterator = extensionMap.find(system);
117 if (extensionMapIterator != extensionMap.end())
118 {
119 extensions = extensionMapIterator->second;
120 return true;
121 }
122 }
123
124 return false;
125}
126
127/**
128 * @brief Write host firmware well-known name
129 *
130 * A wrapper around std::filesystem::create_symlink that avoids EEXIST by
131 * deleting any pre-existing file.
132 *
133 * @param[in] linkTarget The link target argument to
134 * std::filesystem::create_symlink
135 * @param[in] linkPath The link path argument to std::filesystem::create_symlink
136 * @param[in] errorCallback A callback made in the event of filesystem errors.
137 */
138void writeLink(const std::filesystem::path& linkTarget,
139 const std::filesystem::path& linkPath,
140 const ErrorCallbackType& errorCallback)
141{
142 std::error_code ec;
143
144 // remove files with the same name as the symlink to be created,
145 // otherwise symlink will fail with EEXIST.
146 if (!std::filesystem::remove(linkPath, ec))
147 {
148 if (ec)
149 {
150 makeCallback(errorCallback, linkPath, ec);
151 return;
152 }
153 }
154
155 std::filesystem::create_symlink(linkTarget, linkPath, ec);
156 if (ec)
157 {
158 makeCallback(errorCallback, linkPath, ec);
159 return;
160 }
161}
162
163/**
164 * @brief Find host firmware blob files that need well-known names
165 *
166 * The IBM host firmware runtime looks for data and/or additional code while
167 * bootstraping in files with well-known names. findLinks uses the provided
168 * extensions argument to find host firmware blob files that require a
169 * well-known name. When a blob is found, issue the provided callback
170 * (typically a function that will write a symlink).
171 *
172 * @param[in] hostFirmwareDirectory The directory in which findLinks should
173 * look for host firmware blob files that need well-known names.
174 * @param[in] extentions The extensions of the firmware blob files denote a
175 * host firmware blob file requires a well-known name.
176 * @param[in] errorCallback A callback made in the event of filesystem errors.
177 * @param[in] linkCallback A callback made when host firmware blob files
178 * needing a well known name are found.
179 */
180void findLinks(const std::filesystem::path& hostFirmwareDirectory,
181 const std::vector<std::string>& extensions,
182 const ErrorCallbackType& errorCallback,
183 const LinkCallbackType& linkCallback)
184{
185 std::error_code ec;
186 std::filesystem::directory_iterator directoryIterator(hostFirmwareDirectory,
187 ec);
188 if (ec)
189 {
190 makeCallback(errorCallback, hostFirmwareDirectory, ec);
191 return;
192 }
193
Adriana Kobylakfdc91fa2021-05-20 16:00:45 +0000194 // Create a symlink from HBB to the corresponding LID file if it exists
195 static const auto hbbLid = "81e0065a.lid";
196 auto hbbLidPath = hostFirmwareDirectory / hbbLid;
197 if (std::filesystem::exists(hbbLidPath))
198 {
199 static const auto hbbName = "HBB";
200 auto hbbLinkPath = hostFirmwareDirectory / hbbName;
201 makeCallback(linkCallback, hbbLid, hbbLinkPath, errorCallback);
202 }
203
Brad Bishop099543e2020-11-09 15:37:58 -0500204 for (; directoryIterator != std::filesystem::end(directoryIterator);
205 directoryIterator.increment(ec))
206 {
207 const auto& file = directoryIterator->path();
208 if (ec)
209 {
210 makeCallback(errorCallback, file, ec);
211 // quit here if the increment call failed otherwise the loop may
212 // never finish
213 break;
214 }
215
216 if (std::find(extensions.begin(), extensions.end(), file.extension()) ==
217 extensions.end())
218 {
219 // this file doesn't have an extension or doesn't match any of the
220 // provided extensions.
221 continue;
222 }
223
224 auto linkPath(file.parent_path().append(
225 static_cast<const std::string&>(file.stem())));
226
227 makeCallback(linkCallback, file.filename(), linkPath, errorCallback);
228 }
229}
230
231/**
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000232 * @brief Parse the elements json file and construct a string with the data to
233 * be used to update the bios attribute table.
234 *
235 * @param[in] elementsJsonFilePath - The path to the host firmware json file.
236 * @param[in] extensions - The extensions of the firmware blob files.
Adriana Kobylak53a27392021-06-14 17:42:40 +0000237 */
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000238std::string getBiosAttrStr(const std::filesystem::path& elementsJsonFilePath,
239 const std::vector<std::string>& extensions)
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000240{
241 std::string biosAttrStr{};
242
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000243 std::ifstream jsonFile(elementsJsonFilePath.c_str());
244 if (!jsonFile)
245 {
246 return {};
247 }
248
249 std::map<std::string, std::string> attr;
250 auto data = nlohmann::json::parse(jsonFile, nullptr, false);
251 if (data.is_discarded())
252 {
253 log<level::ERR>("Error parsing JSON file",
254 entry("FILE=%s", elementsJsonFilePath.c_str()));
255 return {};
256 }
257
258 // .get requires a non-const iterator
259 for (auto& iter : data["lids"])
260 {
261 std::string name{};
262 std::string lid{};
263
264 try
265 {
266 name = iter["element_name"].get<std::string>();
267 lid = iter["short_lid_name"].get<std::string>();
268 }
269 catch (std::exception& e)
270 {
271 // Possibly the element or lid name field was not found
272 log<level::ERR>("Error reading JSON field",
273 entry("FILE=%s", elementsJsonFilePath.c_str()),
274 entry("ERROR=%s", e.what()));
275 continue;
276 }
277
278 // The elements with the ipl extension have higher priority. Therefore
279 // Use operator[] to overwrite value if an entry for it already exists.
280 // Ex: if the JSON contains an entry A.P10 followed by A.P10.iplTime,
281 // the lid value for the latter one will be overwrite the value of the
282 // first one.
283 constexpr auto iplExtension = ".iplTime";
284 std::filesystem::path path(name);
285 if (path.extension() == iplExtension)
286 {
287 // Some elements have an additional extension, ex: .P10.iplTime
288 // Strip off the ipl extension with stem(), then check if there is
289 // an additional extension with extension().
290 if (!path.stem().extension().empty())
291 {
292 // Check if the extension matches the extensions for this system
293 if (std::find(extensions.begin(), extensions.end(),
294 path.stem().extension()) == extensions.end())
295 {
296 continue;
297 }
298 }
299 // Get the element name without extensions by calling stem() twice
300 // since stem() returns the base name if no periods are found.
301 // Therefore both "element.P10" and "element.P10.iplTime" would
302 // become "element".
303 attr[path.stem().stem()] = lid;
304 continue;
305 }
306
307 // Process all other extensions. The extension should match the list of
308 // supported extensions for this system. Use .insert() to only add
309 // entries that do not exist, so to not overwrite the values that may
310 // had been added that had the ipl extension.
311 if (std::find(extensions.begin(), extensions.end(), path.extension()) !=
312 extensions.end())
313 {
314 attr.insert({path.stem(), lid});
315 }
316 }
317 for (const auto& a : attr)
318 {
319 // Build the bios attribute string with format:
320 // "element1=lid1,element2=lid2,elementN=lidN,"
321 biosAttrStr += a.first + "=" + a.second + ",";
322 }
323
324 return biosAttrStr;
325}
326
327/**
328 * @brief Set the bios attribute table with details of the host firmware data
329 * for this system.
330 *
331 * @param[in] elementsJsonFilePath - The path to the host firmware json file.
332 * @param[in] extentions - The extensions of the firmware blob files.
333 */
334void setBiosAttr(const std::filesystem::path& elementsJsonFilePath,
335 const std::vector<std::string>& extensions)
336{
337 auto biosAttrStr = getBiosAttrStr(elementsJsonFilePath, extensions);
338
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000339 constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager";
340 constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager";
341 constexpr auto dbusAttrName = "hb_lid_ids";
342 constexpr auto dbusAttrType =
343 "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
344
345 using PendingAttributesType = std::vector<std::pair<
346 std::string, std::tuple<std::string, std::variant<std::string>>>>;
347 PendingAttributesType pendingAttributes;
348 pendingAttributes.emplace_back(std::make_pair(
349 dbusAttrName, std::make_tuple(dbusAttrType, biosAttrStr)));
350
351 auto bus = sdbusplus::bus::new_default();
352 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
353 MAPPER_INTERFACE, "GetObject");
354 method.append(biosConfigPath, std::vector<std::string>({biosConfigIntf}));
355 std::vector<std::pair<std::string, std::vector<std::string>>> response;
356 try
357 {
358 auto reply = bus.call(method);
359 reply.read(response);
360 if (response.empty())
361 {
362 log<level::ERR>("Error reading mapper response",
363 entry("PATH=%s", biosConfigPath),
364 entry("INTERFACE=%s", biosConfigIntf));
365 return;
366 }
367 auto method = bus.new_method_call((response.begin()->first).c_str(),
368 biosConfigPath,
369 SYSTEMD_PROPERTY_INTERFACE, "Set");
370 method.append(biosConfigIntf, "PendingAttributes",
371 std::variant<PendingAttributesType>(pendingAttributes));
372 bus.call(method);
373 }
374 catch (const sdbusplus::exception::SdBusError& e)
375 {
376 log<level::ERR>("Error setting the bios attribute",
377 entry("ERROR=%s", e.what()),
378 entry("ATTRIBUTE=%s", dbusAttrName));
379 return;
380 }
381}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000382
383/**
Brad Bishop099543e2020-11-09 15:37:58 -0500384 * @brief Make callbacks on
385 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
386 *
387 * Look for an instance of
388 * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
389 * argument and if found, issue the provided callback.
390 *
391 * @param[in] interfacesAndProperties the interfaces in which to look for an
392 * instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem
393 * @param[in] callback the user callback to make if
394 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
395 * interfacesAndProperties
396 * @return true if interfacesAndProperties contained an instance of
397 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
398 */
399bool maybeCall(const std::map<std::string,
400 std::map<std::string,
401 std::variant<std::vector<std::string>>>>&
402 interfacesAndProperties,
403 const MaybeCallCallbackType& callback)
404{
405 using namespace std::string_literals;
406
407 static const auto interfaceName =
408 "xyz.openbmc_project.Configuration.IBMCompatibleSystem"s;
409 auto interfaceIterator = interfacesAndProperties.find(interfaceName);
410 if (interfaceIterator == interfacesAndProperties.cend())
411 {
412 // IBMCompatibleSystem interface not found, so instruct the caller to
413 // keep waiting or try again later.
414 return false;
415 }
416 auto propertyIterator = interfaceIterator->second.find("Names"s);
417 if (propertyIterator == interfaceIterator->second.cend())
418 {
419 // The interface exists but the property doesn't. This is a bug in the
420 // IBMCompatibleSystem implementation. The caller should not try
421 // again.
422 std::cerr << "Names property not implemented on " << interfaceName
423 << "\n";
424 return true;
425 }
426
427 const auto& ibmCompatibleSystem =
428 std::get<std::vector<std::string>>(propertyIterator->second);
429 if (callback)
430 {
431 callback(ibmCompatibleSystem);
432 }
433
434 // IBMCompatibleSystem found and callback issued.
435 return true;
436}
437
438/**
439 * @brief Make callbacks on
440 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
441 *
442 * Look for an instance of
443 * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
444 * argument and if found, issue the provided callback.
445 *
446 * @param[in] message the DBus message in which to look for an instance of
447 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
448 * @param[in] callback the user callback to make if
449 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
450 * message
451 * @return true if message contained an instance of
452 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
453 */
454bool maybeCallMessage(sdbusplus::message::message& message,
455 const MaybeCallCallbackType& callback)
456{
457 std::map<std::string,
458 std::map<std::string, std::variant<std::vector<std::string>>>>
459 interfacesAndProperties;
460 sdbusplus::message::object_path _;
461 message.read(_, interfacesAndProperties);
462 return maybeCall(interfacesAndProperties, callback);
463}
464
465/**
466 * @brief Determine system support for host firmware well-known names.
467 *
468 * Using the provided extensionMap and
469 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if
470 * well-known names for host firmare blob files are necessary and if so, create
471 * them.
472 *
473 * @param[in] extensionMap a map of
474 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
475 * file extensions.
476 * @param[in] hostFirmwareDirectory The directory in which findLinks should
477 * look for host firmware blob files that need well-known names.
478 * @param[in] ibmCompatibleSystem The names property of an instance of
479 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
480 * @param[in] errorCallback A callback made in the event of filesystem errors.
481 */
482void maybeMakeLinks(
483 const std::map<std::string, std::vector<std::string>>& extensionMap,
484 const std::filesystem::path& hostFirmwareDirectory,
485 const std::vector<std::string>& ibmCompatibleSystem,
486 const ErrorCallbackType& errorCallback)
487{
488 std::vector<std::string> extensions;
489 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
490 extensions))
491 {
492 findLinks(hostFirmwareDirectory, extensions, errorCallback, writeLink);
493 }
494}
495
496/**
Adriana Kobylak53a27392021-06-14 17:42:40 +0000497 * @brief Determine system support for updating the bios attribute table.
498 *
499 * Using the provided extensionMap and
500 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if the bios
501 * attribute table needs to be updated.
502 *
503 * @param[in] extensionMap a map of
504 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
505 * file extensions.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000506 * @param[in] elementsJsonFilePath The file path to the json file
Adriana Kobylak53a27392021-06-14 17:42:40 +0000507 * @param[in] ibmCompatibleSystem The names property of an instance of
508 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
509 */
510void maybeSetBiosAttr(
511 const std::map<std::string, std::vector<std::string>>& extensionMap,
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000512 const std::filesystem::path& elementsJsonFilePath,
Adriana Kobylak53a27392021-06-14 17:42:40 +0000513 const std::vector<std::string>& ibmCompatibleSystem)
514{
515 std::vector<std::string> extensions;
516 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
517 extensions))
518 {
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000519 setBiosAttr(elementsJsonFilePath, extensions);
Adriana Kobylak53a27392021-06-14 17:42:40 +0000520 }
521}
522
523/**
Brad Bishop099543e2020-11-09 15:37:58 -0500524 * @brief process host firmware
525 *
526 * Allocate a callback context and register for DBus.ObjectManager Interfaces
527 * added signals from entity manager.
528 *
529 * Check the current entity manager object tree for a
530 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instance (entity
531 * manager will be dbus activated if it is not running). If one is found,
532 * determine if symlinks need to be created and create them. Instruct the
533 * program event loop to exit.
534 *
535 * If no instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
536 * found return the callback context to main, where the program will sleep
537 * until the callback is invoked one or more times and instructs the program
538 * event loop to exit when
539 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is added.
540 *
541 * @param[in] bus a DBus client connection
542 * @param[in] extensionMap a map of
543 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
544 * file extensions.
545 * @param[in] hostFirmwareDirectory The directory in which processHostFirmware
546 * should look for blob files.
547 * @param[in] errorCallback A callback made in the event of filesystem errors.
548 * @param[in] loop a program event loop
549 * @return nullptr if an instance of
550 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found, otherwise a
551 * pointer to an sdbusplus match object.
552 */
553std::shared_ptr<void> processHostFirmware(
554 sdbusplus::bus::bus& bus,
555 std::map<std::string, std::vector<std::string>> extensionMap,
556 std::filesystem::path hostFirmwareDirectory,
557 ErrorCallbackType errorCallback, sdeventplus::Event& loop)
558{
559 // ownership of extensionMap, hostFirmwareDirectory and errorCallback can't
560 // be transfered to the match callback because they are needed in the non
561 // async part of this function below, so they need to be moved to the heap.
562 auto pExtensionMap =
563 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
564 auto pHostFirmwareDirectory =
565 std::make_shared<decltype(hostFirmwareDirectory)>(
566 std::move(hostFirmwareDirectory));
567 auto pErrorCallback =
568 std::make_shared<decltype(errorCallback)>(std::move(errorCallback));
569
570 // register for a callback in case the IBMCompatibleSystem interface has
571 // not yet been published by entity manager.
572 auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match::match>(
573 bus,
574 sdbusplus::bus::match::rules::interfacesAdded() +
575 sdbusplus::bus::match::rules::sender(
576 "xyz.openbmc_project.EntityManager"),
577 [pExtensionMap, pHostFirmwareDirectory, pErrorCallback,
578 &loop](auto& message) {
579 // bind the extension map, host firmware directory, and error
580 // callback to the maybeMakeLinks function.
581 auto maybeMakeLinksWithArgsBound =
582 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
583 std::cref(*pHostFirmwareDirectory),
584 std::placeholders::_1, std::cref(*pErrorCallback));
585
586 // if the InterfacesAdded message contains an an instance of
587 // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to
588 // see if links are necessary on this system and if so, create
589 // them.
590 if (maybeCallMessage(message, maybeMakeLinksWithArgsBound))
591 {
592 // The IBMCompatibleSystem interface was found and the links
593 // were created if applicable. Instruct the event loop /
594 // subcommand to exit.
595 loop.exit(0);
596 }
597 });
598
599 // now that we'll get a callback in the event of an InterfacesAdded signal
600 // (potentially containing
601 // xyz.openbmc_project.Configuration.IBMCompatibleSystem), activate entity
602 // manager if it isn't running and enumerate its objects
603 auto getManagedObjects = bus.new_method_call(
604 "xyz.openbmc_project.EntityManager", "/",
605 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Brad Bishop099543e2020-11-09 15:37:58 -0500606 std::map<std::string,
607 std::map<std::string, std::variant<std::vector<std::string>>>>
608 interfacesAndProperties;
609 std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)>
610 objects;
Adriana Kobylakc79fa912021-06-22 15:37:50 +0000611 try
612 {
613 auto reply = bus.call(getManagedObjects);
614 reply.read(objects);
615 }
616 catch (const sdbusplus::exception::SdBusError& e)
617 {
618 // Error querying the EntityManager interface. Return the match to have
619 // the callback run if/when the interface appears in D-Bus.
620 return interfacesAddedMatch;
621 }
Brad Bishop099543e2020-11-09 15:37:58 -0500622
623 // bind the extension map, host firmware directory, and error callback to
624 // the maybeMakeLinks function.
625 auto maybeMakeLinksWithArgsBound =
626 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
627 std::cref(*pHostFirmwareDirectory), std::placeholders::_1,
628 std::cref(*pErrorCallback));
629
630 for (const auto& pair : objects)
631 {
632 std::tie(std::ignore, interfacesAndProperties) = pair;
633 // if interfacesAndProperties contains an an instance of
634 // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to see
635 // if links are necessary on this system and if so, create them
636 if (maybeCall(interfacesAndProperties, maybeMakeLinksWithArgsBound))
637 {
638 // The IBMCompatibleSystem interface is already on the bus and the
639 // links were created if applicable. Instruct the event loop to
640 // exit.
641 loop.exit(0);
642 // The match object isn't needed anymore, so destroy it on return.
643 return nullptr;
644 }
645 }
646
647 // The IBMCompatibleSystem interface has not yet been published. Move
648 // ownership of the match callback to the caller.
649 return interfacesAddedMatch;
650}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000651
652/**
653 * @brief Update the Bios Attribute Table
654 *
655 * If an instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
656 * found, update the Bios Attribute Table with the appropriate host firmware
657 * data.
658 *
659 * @param[in] bus - D-Bus client connection.
660 * @param[in] extensionMap - Map of IBMCompatibleSystem names and host firmware
661 * file extensions.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000662 * @param[in] elementsJsonFilePath - The Path to the json file
Adriana Kobylak53a27392021-06-14 17:42:40 +0000663 * @param[in] loop - Program event loop.
664 * @return nullptr
665 */
666std::shared_ptr<void> updateBiosAttrTable(
667 sdbusplus::bus::bus& bus,
668 std::map<std::string, std::vector<std::string>> extensionMap,
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000669 std::filesystem::path elementsJsonFilePath, sdeventplus::Event& loop)
Adriana Kobylak53a27392021-06-14 17:42:40 +0000670{
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000671 constexpr auto pldmPath = "/xyz/openbmc_project/pldm";
672
Adriana Kobylak53a27392021-06-14 17:42:40 +0000673 auto pExtensionMap =
674 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000675 auto pElementsJsonFilePath =
676 std::make_shared<decltype(elementsJsonFilePath)>(
677 std::move(elementsJsonFilePath));
Adriana Kobylak53a27392021-06-14 17:42:40 +0000678
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000679 // Entity Manager is needed to get the list of supported extensions. Add a
680 // match to monitor interfaces added in case it's not running yet.
681 auto maybeSetAttrWithArgsBound =
682 std::bind(maybeSetBiosAttr, std::cref(*pExtensionMap),
683 std::cref(*pElementsJsonFilePath), std::placeholders::_1);
684
685 auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match::match>(
686 bus,
687 sdbusplus::bus::match::rules::interfacesAdded() +
688 sdbusplus::bus::match::rules::sender(
689 "xyz.openbmc_project.EntityManager"),
690 [pldmPath, pExtensionMap, pElementsJsonFilePath,
691 maybeSetAttrWithArgsBound, &loop](auto& message) {
692 auto bus = sdbusplus::bus::new_default();
693 auto pldmObject = getObject(bus, pldmPath);
694 if (pldmObject.empty())
695 {
696 return;
697 }
698 if (maybeCallMessage(message, maybeSetAttrWithArgsBound))
699 {
700 loop.exit(0);
701 }
702 });
703
Adriana Kobylakfd4a6082021-06-21 15:53:34 +0000704 // The BIOS attribute table can only be updated if PLDM is running because
705 // PLDM is the one that exposes this property. Return if it's not running.
Adriana Kobylakfd4a6082021-06-21 15:53:34 +0000706 auto pldmObject = getObject(bus, pldmPath);
707 if (pldmObject.empty())
708 {
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000709 return interfacesAddedMatch;
Adriana Kobylakfd4a6082021-06-21 15:53:34 +0000710 }
711
Adriana Kobylak53a27392021-06-14 17:42:40 +0000712 auto getManagedObjects = bus.new_method_call(
713 "xyz.openbmc_project.EntityManager", "/",
714 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
715 std::map<std::string,
716 std::map<std::string, std::variant<std::vector<std::string>>>>
717 interfacesAndProperties;
718 std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)>
719 objects;
720 try
721 {
722 auto reply = bus.call(getManagedObjects);
723 reply.read(objects);
724 }
725 catch (const sdbusplus::exception::SdBusError& e)
726 {}
727
Adriana Kobylak53a27392021-06-14 17:42:40 +0000728 for (const auto& pair : objects)
729 {
730 std::tie(std::ignore, interfacesAndProperties) = pair;
731 if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound))
732 {
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000733 loop.exit(0);
734 return nullptr;
Adriana Kobylak53a27392021-06-14 17:42:40 +0000735 }
736 }
737
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000738 return interfacesAddedMatch;
Adriana Kobylak53a27392021-06-14 17:42:40 +0000739}
740
Brad Bishop099543e2020-11-09 15:37:58 -0500741} // namespace process_hostfirmware
742} // namespace functions