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