blob: db7f52926129362f3b7d2fce6f14e104290b1bd1 [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 }
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 + ",";
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
370 return biosAttrStr;
371}
372
373/**
374 * @brief Set the bios attribute table with details of the host firmware data
375 * for this system.
376 *
377 * @param[in] elementsJsonFilePath - The path to the host firmware json file.
378 * @param[in] extentions - The extensions of the firmware blob files.
379 */
380void setBiosAttr(const std::filesystem::path& elementsJsonFilePath,
381 const std::vector<std::string>& extensions)
382{
383 auto biosAttrStr = getBiosAttrStr(elementsJsonFilePath, extensions);
384
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000385 constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager";
386 constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager";
387 constexpr auto dbusAttrName = "hb_lid_ids";
388 constexpr auto dbusAttrType =
389 "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
390
391 using PendingAttributesType = std::vector<std::pair<
392 std::string, std::tuple<std::string, std::variant<std::string>>>>;
393 PendingAttributesType pendingAttributes;
394 pendingAttributes.emplace_back(std::make_pair(
395 dbusAttrName, std::make_tuple(dbusAttrType, biosAttrStr)));
396
397 auto bus = sdbusplus::bus::new_default();
398 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
399 MAPPER_INTERFACE, "GetObject");
400 method.append(biosConfigPath, std::vector<std::string>({biosConfigIntf}));
401 std::vector<std::pair<std::string, std::vector<std::string>>> response;
402 try
403 {
404 auto reply = bus.call(method);
405 reply.read(response);
406 if (response.empty())
407 {
408 log<level::ERR>("Error reading mapper response",
409 entry("PATH=%s", biosConfigPath),
410 entry("INTERFACE=%s", biosConfigIntf));
411 return;
412 }
413 auto method = bus.new_method_call((response.begin()->first).c_str(),
414 biosConfigPath,
415 SYSTEMD_PROPERTY_INTERFACE, "Set");
416 method.append(biosConfigIntf, "PendingAttributes",
417 std::variant<PendingAttributesType>(pendingAttributes));
418 bus.call(method);
419 }
Patrick Williams7b5685d2021-09-02 09:32:14 -0500420 catch (const sdbusplus::exception::exception& e)
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000421 {
422 log<level::ERR>("Error setting the bios attribute",
423 entry("ERROR=%s", e.what()),
424 entry("ATTRIBUTE=%s", dbusAttrName));
425 return;
426 }
427}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000428
429/**
Brad Bishop099543e2020-11-09 15:37:58 -0500430 * @brief Make callbacks on
431 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
432 *
433 * Look for an instance of
434 * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
435 * argument and if found, issue the provided callback.
436 *
437 * @param[in] interfacesAndProperties the interfaces in which to look for an
438 * instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem
439 * @param[in] callback the user callback to make if
440 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
441 * interfacesAndProperties
442 * @return true if interfacesAndProperties contained an instance of
443 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
444 */
445bool maybeCall(const std::map<std::string,
446 std::map<std::string,
447 std::variant<std::vector<std::string>>>>&
448 interfacesAndProperties,
449 const MaybeCallCallbackType& callback)
450{
451 using namespace std::string_literals;
452
453 static const auto interfaceName =
454 "xyz.openbmc_project.Configuration.IBMCompatibleSystem"s;
455 auto interfaceIterator = interfacesAndProperties.find(interfaceName);
456 if (interfaceIterator == interfacesAndProperties.cend())
457 {
458 // IBMCompatibleSystem interface not found, so instruct the caller to
459 // keep waiting or try again later.
460 return false;
461 }
462 auto propertyIterator = interfaceIterator->second.find("Names"s);
463 if (propertyIterator == interfaceIterator->second.cend())
464 {
465 // The interface exists but the property doesn't. This is a bug in the
466 // IBMCompatibleSystem implementation. The caller should not try
467 // again.
468 std::cerr << "Names property not implemented on " << interfaceName
469 << "\n";
470 return true;
471 }
472
473 const auto& ibmCompatibleSystem =
474 std::get<std::vector<std::string>>(propertyIterator->second);
475 if (callback)
476 {
477 callback(ibmCompatibleSystem);
478 }
479
480 // IBMCompatibleSystem found and callback issued.
481 return true;
482}
483
484/**
485 * @brief Make callbacks on
486 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
487 *
488 * Look for an instance of
489 * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
490 * argument and if found, issue the provided callback.
491 *
492 * @param[in] message the DBus message in which to look for an instance of
493 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
494 * @param[in] callback the user callback to make if
495 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
496 * message
497 * @return true if message contained an instance of
498 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
499 */
500bool maybeCallMessage(sdbusplus::message::message& message,
501 const MaybeCallCallbackType& callback)
502{
503 std::map<std::string,
504 std::map<std::string, std::variant<std::vector<std::string>>>>
505 interfacesAndProperties;
506 sdbusplus::message::object_path _;
507 message.read(_, interfacesAndProperties);
508 return maybeCall(interfacesAndProperties, callback);
509}
510
511/**
512 * @brief Determine system support for host firmware well-known names.
513 *
514 * Using the provided extensionMap and
515 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if
516 * well-known names for host firmare blob files are necessary and if so, create
517 * them.
518 *
519 * @param[in] extensionMap a map of
520 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
521 * file extensions.
522 * @param[in] hostFirmwareDirectory The directory in which findLinks should
523 * look for host firmware blob files that need well-known names.
524 * @param[in] ibmCompatibleSystem The names property of an instance of
525 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
526 * @param[in] errorCallback A callback made in the event of filesystem errors.
527 */
528void maybeMakeLinks(
529 const std::map<std::string, std::vector<std::string>>& extensionMap,
530 const std::filesystem::path& hostFirmwareDirectory,
531 const std::vector<std::string>& ibmCompatibleSystem,
532 const ErrorCallbackType& errorCallback)
533{
534 std::vector<std::string> extensions;
535 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
536 extensions))
537 {
538 findLinks(hostFirmwareDirectory, extensions, errorCallback, writeLink);
539 }
540}
541
542/**
Adriana Kobylak53a27392021-06-14 17:42:40 +0000543 * @brief Determine system support for updating the bios attribute table.
544 *
545 * Using the provided extensionMap and
546 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if the bios
547 * attribute table needs to be updated.
548 *
549 * @param[in] extensionMap a map of
550 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
551 * file extensions.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000552 * @param[in] elementsJsonFilePath The file path to the json file
Adriana Kobylak53a27392021-06-14 17:42:40 +0000553 * @param[in] ibmCompatibleSystem The names property of an instance of
554 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
555 */
556void maybeSetBiosAttr(
557 const std::map<std::string, std::vector<std::string>>& extensionMap,
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000558 const std::filesystem::path& elementsJsonFilePath,
Adriana Kobylak53a27392021-06-14 17:42:40 +0000559 const std::vector<std::string>& ibmCompatibleSystem)
560{
561 std::vector<std::string> extensions;
562 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
563 extensions))
564 {
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000565 setBiosAttr(elementsJsonFilePath, extensions);
Adriana Kobylak53a27392021-06-14 17:42:40 +0000566 }
567}
568
569/**
Brad Bishop099543e2020-11-09 15:37:58 -0500570 * @brief process host firmware
571 *
572 * Allocate a callback context and register for DBus.ObjectManager Interfaces
573 * added signals from entity manager.
574 *
575 * Check the current entity manager object tree for a
576 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instance (entity
577 * manager will be dbus activated if it is not running). If one is found,
578 * determine if symlinks need to be created and create them. Instruct the
579 * program event loop to exit.
580 *
581 * If no instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
582 * found return the callback context to main, where the program will sleep
583 * until the callback is invoked one or more times and instructs the program
584 * event loop to exit when
585 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is added.
586 *
587 * @param[in] bus a DBus client connection
588 * @param[in] extensionMap a map of
589 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
590 * file extensions.
591 * @param[in] hostFirmwareDirectory The directory in which processHostFirmware
592 * should look for blob files.
593 * @param[in] errorCallback A callback made in the event of filesystem errors.
594 * @param[in] loop a program event loop
595 * @return nullptr if an instance of
596 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found, otherwise a
597 * pointer to an sdbusplus match object.
598 */
599std::shared_ptr<void> processHostFirmware(
600 sdbusplus::bus::bus& bus,
601 std::map<std::string, std::vector<std::string>> extensionMap,
602 std::filesystem::path hostFirmwareDirectory,
603 ErrorCallbackType errorCallback, sdeventplus::Event& loop)
604{
605 // ownership of extensionMap, hostFirmwareDirectory and errorCallback can't
606 // be transfered to the match callback because they are needed in the non
607 // async part of this function below, so they need to be moved to the heap.
608 auto pExtensionMap =
609 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
610 auto pHostFirmwareDirectory =
611 std::make_shared<decltype(hostFirmwareDirectory)>(
612 std::move(hostFirmwareDirectory));
613 auto pErrorCallback =
614 std::make_shared<decltype(errorCallback)>(std::move(errorCallback));
615
616 // register for a callback in case the IBMCompatibleSystem interface has
617 // not yet been published by entity manager.
618 auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match::match>(
619 bus,
620 sdbusplus::bus::match::rules::interfacesAdded() +
621 sdbusplus::bus::match::rules::sender(
622 "xyz.openbmc_project.EntityManager"),
623 [pExtensionMap, pHostFirmwareDirectory, pErrorCallback,
624 &loop](auto& message) {
625 // bind the extension map, host firmware directory, and error
626 // callback to the maybeMakeLinks function.
627 auto maybeMakeLinksWithArgsBound =
628 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
629 std::cref(*pHostFirmwareDirectory),
630 std::placeholders::_1, std::cref(*pErrorCallback));
631
632 // if the InterfacesAdded message contains an an instance of
633 // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to
634 // see if links are necessary on this system and if so, create
635 // them.
636 if (maybeCallMessage(message, maybeMakeLinksWithArgsBound))
637 {
638 // The IBMCompatibleSystem interface was found and the links
639 // were created if applicable. Instruct the event loop /
640 // subcommand to exit.
641 loop.exit(0);
642 }
643 });
644
645 // now that we'll get a callback in the event of an InterfacesAdded signal
646 // (potentially containing
647 // xyz.openbmc_project.Configuration.IBMCompatibleSystem), activate entity
648 // manager if it isn't running and enumerate its objects
649 auto getManagedObjects = bus.new_method_call(
650 "xyz.openbmc_project.EntityManager", "/",
651 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Brad Bishop099543e2020-11-09 15:37:58 -0500652 std::map<std::string,
653 std::map<std::string, std::variant<std::vector<std::string>>>>
654 interfacesAndProperties;
655 std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)>
656 objects;
Adriana Kobylakc79fa912021-06-22 15:37:50 +0000657 try
658 {
659 auto reply = bus.call(getManagedObjects);
660 reply.read(objects);
661 }
Patrick Williams7b5685d2021-09-02 09:32:14 -0500662 catch (const sdbusplus::exception::exception& e)
Adriana Kobylakc79fa912021-06-22 15:37:50 +0000663 {
664 // Error querying the EntityManager interface. Return the match to have
665 // the callback run if/when the interface appears in D-Bus.
666 return interfacesAddedMatch;
667 }
Brad Bishop099543e2020-11-09 15:37:58 -0500668
669 // bind the extension map, host firmware directory, and error callback to
670 // the maybeMakeLinks function.
671 auto maybeMakeLinksWithArgsBound =
672 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
673 std::cref(*pHostFirmwareDirectory), std::placeholders::_1,
674 std::cref(*pErrorCallback));
675
676 for (const auto& pair : objects)
677 {
678 std::tie(std::ignore, interfacesAndProperties) = pair;
679 // if interfacesAndProperties contains an an instance of
680 // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to see
681 // if links are necessary on this system and if so, create them
682 if (maybeCall(interfacesAndProperties, maybeMakeLinksWithArgsBound))
683 {
684 // The IBMCompatibleSystem interface is already on the bus and the
685 // links were created if applicable. Instruct the event loop to
686 // exit.
687 loop.exit(0);
688 // The match object isn't needed anymore, so destroy it on return.
689 return nullptr;
690 }
691 }
692
693 // The IBMCompatibleSystem interface has not yet been published. Move
694 // ownership of the match callback to the caller.
695 return interfacesAddedMatch;
696}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000697
698/**
699 * @brief Update the Bios Attribute Table
700 *
701 * If an instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
702 * found, update the Bios Attribute Table with the appropriate host firmware
703 * data.
704 *
705 * @param[in] bus - D-Bus client connection.
706 * @param[in] extensionMap - Map of IBMCompatibleSystem names and host firmware
707 * file extensions.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000708 * @param[in] elementsJsonFilePath - The Path to the json file
Adriana Kobylak53a27392021-06-14 17:42:40 +0000709 * @param[in] loop - Program event loop.
710 * @return nullptr
711 */
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000712std::vector<std::shared_ptr<void>> updateBiosAttrTable(
Adriana Kobylak53a27392021-06-14 17:42:40 +0000713 sdbusplus::bus::bus& bus,
714 std::map<std::string, std::vector<std::string>> extensionMap,
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000715 std::filesystem::path elementsJsonFilePath, sdeventplus::Event& loop)
Adriana Kobylak53a27392021-06-14 17:42:40 +0000716{
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000717 constexpr auto pldmPath = "/xyz/openbmc_project/pldm";
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000718 constexpr auto entityManagerServiceName =
719 "xyz.openbmc_project.EntityManager";
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000720
Adriana Kobylak53a27392021-06-14 17:42:40 +0000721 auto pExtensionMap =
722 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000723 auto pElementsJsonFilePath =
724 std::make_shared<decltype(elementsJsonFilePath)>(
725 std::move(elementsJsonFilePath));
Adriana Kobylak53a27392021-06-14 17:42:40 +0000726
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000727 // Entity Manager is needed to get the list of supported extensions. Add a
728 // match to monitor interfaces added in case it's not running yet.
729 auto maybeSetAttrWithArgsBound =
730 std::bind(maybeSetBiosAttr, std::cref(*pExtensionMap),
731 std::cref(*pElementsJsonFilePath), std::placeholders::_1);
732
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000733 std::vector<std::shared_ptr<void>> matches;
734 matches.emplace_back(std::make_shared<sdbusplus::bus::match::match>(
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000735 bus,
736 sdbusplus::bus::match::rules::interfacesAdded() +
737 sdbusplus::bus::match::rules::sender(
738 "xyz.openbmc_project.EntityManager"),
739 [pldmPath, pExtensionMap, pElementsJsonFilePath,
740 maybeSetAttrWithArgsBound, &loop](auto& message) {
741 auto bus = sdbusplus::bus::new_default();
742 auto pldmObject = getObject(bus, pldmPath);
743 if (pldmObject.empty())
744 {
745 return;
746 }
747 if (maybeCallMessage(message, maybeSetAttrWithArgsBound))
748 {
749 loop.exit(0);
750 }
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000751 }));
752 matches.emplace_back(std::make_shared<sdbusplus::bus::match::match>(
753 bus,
754 sdbusplus::bus::match::rules::nameOwnerChanged() +
755 sdbusplus::bus::match::rules::arg0namespace(
756 "xyz.openbmc_project.PLDM"),
757 [pExtensionMap, pElementsJsonFilePath, maybeSetAttrWithArgsBound,
758 &loop](auto& message) {
759 std::string name;
760 std::string oldOwner;
761 std::string newOwner;
762 message.read(name, oldOwner, newOwner);
763
764 if (newOwner.empty())
765 {
766 return;
767 }
768
769 auto bus = sdbusplus::bus::new_default();
770 InterfacesPropertiesMap interfacesAndProperties;
771 auto objects = getManagedObjects(bus, entityManagerServiceName);
772 for (const auto& pair : objects)
773 {
774 std::tie(std::ignore, interfacesAndProperties) = pair;
775 if (maybeCall(interfacesAndProperties,
776 maybeSetAttrWithArgsBound))
777 {
778 loop.exit(0);
779 }
780 }
781 }));
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000782
Adriana Kobylakfd4a6082021-06-21 15:53:34 +0000783 // The BIOS attribute table can only be updated if PLDM is running because
784 // PLDM is the one that exposes this property. Return if it's not running.
Adriana Kobylakfd4a6082021-06-21 15:53:34 +0000785 auto pldmObject = getObject(bus, pldmPath);
786 if (pldmObject.empty())
787 {
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000788 return matches;
Adriana Kobylakfd4a6082021-06-21 15:53:34 +0000789 }
790
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000791 InterfacesPropertiesMap interfacesAndProperties;
792 auto objects = getManagedObjects(bus, entityManagerServiceName);
Adriana Kobylak53a27392021-06-14 17:42:40 +0000793 for (const auto& pair : objects)
794 {
795 std::tie(std::ignore, interfacesAndProperties) = pair;
796 if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound))
797 {
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000798 loop.exit(0);
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000799 return {};
Adriana Kobylak53a27392021-06-14 17:42:40 +0000800 }
801 }
802
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000803 return matches;
Adriana Kobylak53a27392021-06-14 17:42:40 +0000804}
805
Brad Bishop099543e2020-11-09 15:37:58 -0500806} // namespace process_hostfirmware
807} // namespace functions