blob: 746a2a827977e73e58b57dffb9cf9362433f26c9 [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 }
Patrick Williams7b5685d2021-09-02 09:32:14 -050063 catch (const sdbusplus::exception::exception& e)
Adriana Kobylakfd4a6082021-06-21 15:53:34 +000064 {
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 }
Patrick Williams7b5685d2021-09-02 09:32:14 -050087 catch (const sdbusplus::exception::exception& e)
Adriana Kobylakebf67bf2021-06-21 18:38:17 +000088 {
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 Kobylak4e82bc82021-09-28 19:25:39 +0000223 // Create a symlink for pnor.toc
224 static const auto tocLid = "81e00994.lid";
225 auto tocLidPath = hostFirmwareDirectory / tocLid;
226 if (std::filesystem::exists(tocLidPath))
227 {
228 static const auto tocName = "pnor.toc";
229 auto tocLinkPath = hostFirmwareDirectory / tocName;
230 makeCallback(linkCallback, tocLid, tocLinkPath, 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 }
Patrick Williams97a709b2021-10-06 13:05:40 -0500298 catch (const std::exception& e)
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000299 {
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 + ",";
Adriana Kobylak5dc5d6c2021-07-20 20:21:16 +0000351
352 // Create symlinks from the hostfw elements to their corresponding
353 // lid files if they don't exist
354 auto elementFilePath =
355 std::filesystem::path("/media/hostfw/running") / a.first;
356 if (!std::filesystem::exists(elementFilePath))
357 {
358 std::error_code ec;
359 auto lidName = a.second + ".lid";
360 std::filesystem::create_symlink(lidName, elementFilePath, ec);
361 if (ec)
362 {
363 log<level::ERR>("Error creating symlink",
364 entry("TARGET=%s", lidName.c_str()),
365 entry("LINK=%s", elementFilePath.c_str()));
366 }
367 }
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000368 }
369
George Liua38f6e62021-09-20 08:53:00 +0800370 // Delete the last comma of the bios attribute string
371 if (biosAttrStr.back() == ',')
372 {
373 return biosAttrStr.substr(0, biosAttrStr.length() - 1);
374 }
375
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000376 return biosAttrStr;
377}
378
379/**
380 * @brief Set the bios attribute table with details of the host firmware data
381 * for this system.
382 *
383 * @param[in] elementsJsonFilePath - The path to the host firmware json file.
384 * @param[in] extentions - The extensions of the firmware blob files.
385 */
386void setBiosAttr(const std::filesystem::path& elementsJsonFilePath,
387 const std::vector<std::string>& extensions)
388{
389 auto biosAttrStr = getBiosAttrStr(elementsJsonFilePath, extensions);
390
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000391 constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager";
392 constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager";
393 constexpr auto dbusAttrName = "hb_lid_ids";
394 constexpr auto dbusAttrType =
395 "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
396
397 using PendingAttributesType = std::vector<std::pair<
398 std::string, std::tuple<std::string, std::variant<std::string>>>>;
399 PendingAttributesType pendingAttributes;
400 pendingAttributes.emplace_back(std::make_pair(
401 dbusAttrName, std::make_tuple(dbusAttrType, biosAttrStr)));
402
403 auto bus = sdbusplus::bus::new_default();
404 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
405 MAPPER_INTERFACE, "GetObject");
406 method.append(biosConfigPath, std::vector<std::string>({biosConfigIntf}));
407 std::vector<std::pair<std::string, std::vector<std::string>>> response;
408 try
409 {
410 auto reply = bus.call(method);
411 reply.read(response);
412 if (response.empty())
413 {
414 log<level::ERR>("Error reading mapper response",
415 entry("PATH=%s", biosConfigPath),
416 entry("INTERFACE=%s", biosConfigIntf));
417 return;
418 }
419 auto method = bus.new_method_call((response.begin()->first).c_str(),
420 biosConfigPath,
421 SYSTEMD_PROPERTY_INTERFACE, "Set");
422 method.append(biosConfigIntf, "PendingAttributes",
423 std::variant<PendingAttributesType>(pendingAttributes));
424 bus.call(method);
425 }
Patrick Williams7b5685d2021-09-02 09:32:14 -0500426 catch (const sdbusplus::exception::exception& e)
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000427 {
428 log<level::ERR>("Error setting the bios attribute",
429 entry("ERROR=%s", e.what()),
430 entry("ATTRIBUTE=%s", dbusAttrName));
431 return;
432 }
433}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000434
435/**
Brad Bishop099543e2020-11-09 15:37:58 -0500436 * @brief Make callbacks on
437 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
438 *
439 * Look for an instance of
440 * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
441 * argument and if found, issue the provided callback.
442 *
443 * @param[in] interfacesAndProperties the interfaces in which to look for an
444 * instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem
445 * @param[in] callback the user callback to make if
446 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
447 * interfacesAndProperties
448 * @return true if interfacesAndProperties contained an instance of
449 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
450 */
451bool maybeCall(const std::map<std::string,
452 std::map<std::string,
453 std::variant<std::vector<std::string>>>>&
454 interfacesAndProperties,
455 const MaybeCallCallbackType& callback)
456{
457 using namespace std::string_literals;
458
459 static const auto interfaceName =
460 "xyz.openbmc_project.Configuration.IBMCompatibleSystem"s;
461 auto interfaceIterator = interfacesAndProperties.find(interfaceName);
462 if (interfaceIterator == interfacesAndProperties.cend())
463 {
464 // IBMCompatibleSystem interface not found, so instruct the caller to
465 // keep waiting or try again later.
466 return false;
467 }
468 auto propertyIterator = interfaceIterator->second.find("Names"s);
469 if (propertyIterator == interfaceIterator->second.cend())
470 {
471 // The interface exists but the property doesn't. This is a bug in the
472 // IBMCompatibleSystem implementation. The caller should not try
473 // again.
474 std::cerr << "Names property not implemented on " << interfaceName
475 << "\n";
476 return true;
477 }
478
479 const auto& ibmCompatibleSystem =
480 std::get<std::vector<std::string>>(propertyIterator->second);
481 if (callback)
482 {
483 callback(ibmCompatibleSystem);
484 }
485
486 // IBMCompatibleSystem found and callback issued.
487 return true;
488}
489
490/**
491 * @brief Make callbacks on
492 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
493 *
494 * Look for an instance of
495 * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
496 * argument and if found, issue the provided callback.
497 *
498 * @param[in] message the DBus message in which to look for an instance of
499 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
500 * @param[in] callback the user callback to make if
501 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
502 * message
503 * @return true if message contained an instance of
504 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
505 */
506bool maybeCallMessage(sdbusplus::message::message& message,
507 const MaybeCallCallbackType& callback)
508{
509 std::map<std::string,
510 std::map<std::string, std::variant<std::vector<std::string>>>>
511 interfacesAndProperties;
512 sdbusplus::message::object_path _;
513 message.read(_, interfacesAndProperties);
514 return maybeCall(interfacesAndProperties, callback);
515}
516
517/**
518 * @brief Determine system support for host firmware well-known names.
519 *
520 * Using the provided extensionMap and
521 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if
522 * well-known names for host firmare blob files are necessary and if so, create
523 * them.
524 *
525 * @param[in] extensionMap a map of
526 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
527 * file extensions.
528 * @param[in] hostFirmwareDirectory The directory in which findLinks should
529 * look for host firmware blob files that need well-known names.
530 * @param[in] ibmCompatibleSystem The names property of an instance of
531 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
532 * @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
552 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if the bios
553 * attribute table needs to be updated.
554 *
555 * @param[in] extensionMap a map of
556 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
557 * file 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
560 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
561 */
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 Kobylakae0998f2021-06-16 19:52:24 +0000571 setBiosAttr(elementsJsonFilePath, extensions);
Adriana Kobylak53a27392021-06-14 17:42:40 +0000572 }
573}
574
575/**
Brad Bishop099543e2020-11-09 15:37:58 -0500576 * @brief process host firmware
577 *
578 * Allocate a callback context and register for DBus.ObjectManager Interfaces
579 * added signals from entity manager.
580 *
581 * Check the current entity manager object tree for a
582 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instance (entity
583 * manager will be dbus activated if it is not running). If one is found,
584 * determine if symlinks need to be created and create them. Instruct the
585 * program event loop to exit.
586 *
587 * If no instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
588 * found return the callback context to main, where the program will sleep
589 * until the callback is invoked one or more times and instructs the program
590 * event loop to exit when
591 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is added.
592 *
593 * @param[in] bus a DBus client connection
594 * @param[in] extensionMap a map of
595 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
596 * file extensions.
597 * @param[in] hostFirmwareDirectory The directory in which processHostFirmware
598 * should look for blob files.
599 * @param[in] errorCallback A callback made in the event of filesystem errors.
600 * @param[in] loop a program event loop
601 * @return nullptr if an instance of
602 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found, otherwise a
603 * pointer to an sdbusplus match object.
604 */
605std::shared_ptr<void> processHostFirmware(
606 sdbusplus::bus::bus& bus,
607 std::map<std::string, std::vector<std::string>> extensionMap,
608 std::filesystem::path hostFirmwareDirectory,
609 ErrorCallbackType errorCallback, sdeventplus::Event& loop)
610{
611 // ownership of extensionMap, hostFirmwareDirectory and errorCallback can't
612 // be transfered to the match callback because they are needed in the non
613 // async part of this function below, so they need to be moved to the heap.
614 auto pExtensionMap =
615 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
616 auto pHostFirmwareDirectory =
617 std::make_shared<decltype(hostFirmwareDirectory)>(
618 std::move(hostFirmwareDirectory));
619 auto pErrorCallback =
620 std::make_shared<decltype(errorCallback)>(std::move(errorCallback));
621
622 // register for a callback in case the IBMCompatibleSystem interface has
623 // not yet been published by entity manager.
624 auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match::match>(
625 bus,
626 sdbusplus::bus::match::rules::interfacesAdded() +
627 sdbusplus::bus::match::rules::sender(
628 "xyz.openbmc_project.EntityManager"),
629 [pExtensionMap, pHostFirmwareDirectory, pErrorCallback,
630 &loop](auto& message) {
631 // bind the extension map, host firmware directory, and error
632 // callback to the maybeMakeLinks function.
633 auto maybeMakeLinksWithArgsBound =
634 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
635 std::cref(*pHostFirmwareDirectory),
636 std::placeholders::_1, std::cref(*pErrorCallback));
637
638 // if the InterfacesAdded message contains an an instance of
639 // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to
640 // see if links are necessary on this system and if so, create
641 // them.
642 if (maybeCallMessage(message, maybeMakeLinksWithArgsBound))
643 {
644 // The IBMCompatibleSystem interface was found and the links
645 // were created if applicable. Instruct the event loop /
646 // subcommand to exit.
647 loop.exit(0);
648 }
649 });
650
651 // now that we'll get a callback in the event of an InterfacesAdded signal
652 // (potentially containing
653 // xyz.openbmc_project.Configuration.IBMCompatibleSystem), activate entity
654 // manager if it isn't running and enumerate its objects
655 auto getManagedObjects = bus.new_method_call(
656 "xyz.openbmc_project.EntityManager", "/",
657 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Brad Bishop099543e2020-11-09 15:37:58 -0500658 std::map<std::string,
659 std::map<std::string, std::variant<std::vector<std::string>>>>
660 interfacesAndProperties;
661 std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)>
662 objects;
Adriana Kobylakc79fa912021-06-22 15:37:50 +0000663 try
664 {
665 auto reply = bus.call(getManagedObjects);
666 reply.read(objects);
667 }
Patrick Williams7b5685d2021-09-02 09:32:14 -0500668 catch (const sdbusplus::exception::exception& e)
Adriana Kobylakc79fa912021-06-22 15:37:50 +0000669 {
670 // Error querying the EntityManager interface. Return the match to have
671 // the callback run if/when the interface appears in D-Bus.
672 return interfacesAddedMatch;
673 }
Brad Bishop099543e2020-11-09 15:37:58 -0500674
675 // bind the extension map, host firmware directory, and error callback to
676 // the maybeMakeLinks function.
677 auto maybeMakeLinksWithArgsBound =
678 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
679 std::cref(*pHostFirmwareDirectory), std::placeholders::_1,
680 std::cref(*pErrorCallback));
681
682 for (const auto& pair : objects)
683 {
684 std::tie(std::ignore, interfacesAndProperties) = pair;
685 // if interfacesAndProperties contains an an instance of
686 // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to see
687 // if links are necessary on this system and if so, create them
688 if (maybeCall(interfacesAndProperties, maybeMakeLinksWithArgsBound))
689 {
690 // The IBMCompatibleSystem interface is already on the bus and the
691 // links were created if applicable. Instruct the event loop to
692 // exit.
693 loop.exit(0);
694 // The match object isn't needed anymore, so destroy it on return.
695 return nullptr;
696 }
697 }
698
699 // The IBMCompatibleSystem interface has not yet been published. Move
700 // ownership of the match callback to the caller.
701 return interfacesAddedMatch;
702}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000703
704/**
705 * @brief Update the Bios Attribute Table
706 *
707 * If an instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
708 * found, update the Bios Attribute Table with the appropriate host firmware
709 * data.
710 *
711 * @param[in] bus - D-Bus client connection.
712 * @param[in] extensionMap - Map of IBMCompatibleSystem names and host firmware
713 * file extensions.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000714 * @param[in] elementsJsonFilePath - The Path to the json file
Adriana Kobylak53a27392021-06-14 17:42:40 +0000715 * @param[in] loop - Program event loop.
716 * @return nullptr
717 */
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000718std::vector<std::shared_ptr<void>> updateBiosAttrTable(
Adriana Kobylak53a27392021-06-14 17:42:40 +0000719 sdbusplus::bus::bus& bus,
720 std::map<std::string, std::vector<std::string>> extensionMap,
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000721 std::filesystem::path elementsJsonFilePath, sdeventplus::Event& loop)
Adriana Kobylak53a27392021-06-14 17:42:40 +0000722{
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000723 constexpr auto pldmPath = "/xyz/openbmc_project/pldm";
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000724 constexpr auto entityManagerServiceName =
725 "xyz.openbmc_project.EntityManager";
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000726
Adriana Kobylak53a27392021-06-14 17:42:40 +0000727 auto pExtensionMap =
728 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000729 auto pElementsJsonFilePath =
730 std::make_shared<decltype(elementsJsonFilePath)>(
731 std::move(elementsJsonFilePath));
Adriana Kobylak53a27392021-06-14 17:42:40 +0000732
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000733 // Entity Manager is needed to get the list of supported extensions. Add a
734 // match to monitor interfaces added in case it's not running yet.
735 auto maybeSetAttrWithArgsBound =
736 std::bind(maybeSetBiosAttr, std::cref(*pExtensionMap),
737 std::cref(*pElementsJsonFilePath), std::placeholders::_1);
738
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000739 std::vector<std::shared_ptr<void>> matches;
740 matches.emplace_back(std::make_shared<sdbusplus::bus::match::match>(
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000741 bus,
742 sdbusplus::bus::match::rules::interfacesAdded() +
743 sdbusplus::bus::match::rules::sender(
744 "xyz.openbmc_project.EntityManager"),
745 [pldmPath, pExtensionMap, pElementsJsonFilePath,
746 maybeSetAttrWithArgsBound, &loop](auto& message) {
747 auto bus = sdbusplus::bus::new_default();
748 auto pldmObject = getObject(bus, pldmPath);
749 if (pldmObject.empty())
750 {
751 return;
752 }
753 if (maybeCallMessage(message, maybeSetAttrWithArgsBound))
754 {
755 loop.exit(0);
756 }
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000757 }));
758 matches.emplace_back(std::make_shared<sdbusplus::bus::match::match>(
759 bus,
760 sdbusplus::bus::match::rules::nameOwnerChanged() +
761 sdbusplus::bus::match::rules::arg0namespace(
762 "xyz.openbmc_project.PLDM"),
763 [pExtensionMap, pElementsJsonFilePath, maybeSetAttrWithArgsBound,
764 &loop](auto& message) {
765 std::string name;
766 std::string oldOwner;
767 std::string newOwner;
768 message.read(name, oldOwner, newOwner);
769
770 if (newOwner.empty())
771 {
772 return;
773 }
774
775 auto bus = sdbusplus::bus::new_default();
776 InterfacesPropertiesMap interfacesAndProperties;
777 auto objects = getManagedObjects(bus, entityManagerServiceName);
778 for (const auto& pair : objects)
779 {
780 std::tie(std::ignore, interfacesAndProperties) = pair;
781 if (maybeCall(interfacesAndProperties,
782 maybeSetAttrWithArgsBound))
783 {
784 loop.exit(0);
785 }
786 }
787 }));
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000788
Adriana Kobylakfd4a6082021-06-21 15:53:34 +0000789 // The BIOS attribute table can only be updated if PLDM is running because
790 // PLDM is the one that exposes this property. Return if it's not running.
Adriana Kobylakfd4a6082021-06-21 15:53:34 +0000791 auto pldmObject = getObject(bus, pldmPath);
792 if (pldmObject.empty())
793 {
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000794 return matches;
Adriana Kobylakfd4a6082021-06-21 15:53:34 +0000795 }
796
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000797 InterfacesPropertiesMap interfacesAndProperties;
798 auto objects = getManagedObjects(bus, entityManagerServiceName);
Adriana Kobylak53a27392021-06-14 17:42:40 +0000799 for (const auto& pair : objects)
800 {
801 std::tie(std::ignore, interfacesAndProperties) = pair;
802 if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound))
803 {
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000804 loop.exit(0);
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000805 return {};
Adriana Kobylak53a27392021-06-14 17:42:40 +0000806 }
807 }
808
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000809 return matches;
Adriana Kobylak53a27392021-06-14 17:42:40 +0000810}
811
Brad Bishop099543e2020-11-09 15:37:58 -0500812} // namespace process_hostfirmware
813} // namespace functions