blob: 0404d00dab0cbec127883f6ec3df4c2ddcf47a2b [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
Brad Bishop099543e2020-11-09 15:37:58 -0500223 for (; directoryIterator != std::filesystem::end(directoryIterator);
224 directoryIterator.increment(ec))
225 {
226 const auto& file = directoryIterator->path();
227 if (ec)
228 {
229 makeCallback(errorCallback, file, ec);
230 // quit here if the increment call failed otherwise the loop may
231 // never finish
232 break;
233 }
234
235 if (std::find(extensions.begin(), extensions.end(), file.extension()) ==
236 extensions.end())
237 {
238 // this file doesn't have an extension or doesn't match any of the
239 // provided extensions.
240 continue;
241 }
242
243 auto linkPath(file.parent_path().append(
244 static_cast<const std::string&>(file.stem())));
245
246 makeCallback(linkCallback, file.filename(), linkPath, errorCallback);
247 }
248}
249
250/**
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000251 * @brief Parse the elements json file and construct a string with the data to
252 * be used to update the bios attribute table.
253 *
254 * @param[in] elementsJsonFilePath - The path to the host firmware json file.
255 * @param[in] extensions - The extensions of the firmware blob files.
Adriana Kobylak53a27392021-06-14 17:42:40 +0000256 */
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000257std::string getBiosAttrStr(const std::filesystem::path& elementsJsonFilePath,
258 const std::vector<std::string>& extensions)
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000259{
260 std::string biosAttrStr{};
261
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000262 std::ifstream jsonFile(elementsJsonFilePath.c_str());
263 if (!jsonFile)
264 {
265 return {};
266 }
267
268 std::map<std::string, std::string> attr;
269 auto data = nlohmann::json::parse(jsonFile, nullptr, false);
270 if (data.is_discarded())
271 {
272 log<level::ERR>("Error parsing JSON file",
273 entry("FILE=%s", elementsJsonFilePath.c_str()));
274 return {};
275 }
276
277 // .get requires a non-const iterator
278 for (auto& iter : data["lids"])
279 {
280 std::string name{};
281 std::string lid{};
282
283 try
284 {
285 name = iter["element_name"].get<std::string>();
286 lid = iter["short_lid_name"].get<std::string>();
287 }
288 catch (std::exception& e)
289 {
290 // Possibly the element or lid name field was not found
291 log<level::ERR>("Error reading JSON field",
292 entry("FILE=%s", elementsJsonFilePath.c_str()),
293 entry("ERROR=%s", e.what()));
294 continue;
295 }
296
297 // The elements with the ipl extension have higher priority. Therefore
298 // Use operator[] to overwrite value if an entry for it already exists.
299 // Ex: if the JSON contains an entry A.P10 followed by A.P10.iplTime,
300 // the lid value for the latter one will be overwrite the value of the
301 // first one.
302 constexpr auto iplExtension = ".iplTime";
303 std::filesystem::path path(name);
304 if (path.extension() == iplExtension)
305 {
306 // Some elements have an additional extension, ex: .P10.iplTime
307 // Strip off the ipl extension with stem(), then check if there is
308 // an additional extension with extension().
309 if (!path.stem().extension().empty())
310 {
311 // Check if the extension matches the extensions for this system
312 if (std::find(extensions.begin(), extensions.end(),
313 path.stem().extension()) == extensions.end())
314 {
315 continue;
316 }
317 }
318 // Get the element name without extensions by calling stem() twice
319 // since stem() returns the base name if no periods are found.
320 // Therefore both "element.P10" and "element.P10.iplTime" would
321 // become "element".
322 attr[path.stem().stem()] = lid;
323 continue;
324 }
325
326 // Process all other extensions. The extension should match the list of
327 // supported extensions for this system. Use .insert() to only add
328 // entries that do not exist, so to not overwrite the values that may
329 // had been added that had the ipl extension.
330 if (std::find(extensions.begin(), extensions.end(), path.extension()) !=
331 extensions.end())
332 {
333 attr.insert({path.stem(), lid});
334 }
335 }
336 for (const auto& a : attr)
337 {
338 // Build the bios attribute string with format:
339 // "element1=lid1,element2=lid2,elementN=lidN,"
340 biosAttrStr += a.first + "=" + a.second + ",";
Adriana Kobylak5dc5d6c2021-07-20 20:21:16 +0000341
342 // Create symlinks from the hostfw elements to their corresponding
343 // lid files if they don't exist
344 auto elementFilePath =
345 std::filesystem::path("/media/hostfw/running") / a.first;
346 if (!std::filesystem::exists(elementFilePath))
347 {
348 std::error_code ec;
349 auto lidName = a.second + ".lid";
350 std::filesystem::create_symlink(lidName, elementFilePath, ec);
351 if (ec)
352 {
353 log<level::ERR>("Error creating symlink",
354 entry("TARGET=%s", lidName.c_str()),
355 entry("LINK=%s", elementFilePath.c_str()));
356 }
357 }
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000358 }
359
360 return biosAttrStr;
361}
362
363/**
364 * @brief Set the bios attribute table with details of the host firmware data
365 * for this system.
366 *
367 * @param[in] elementsJsonFilePath - The path to the host firmware json file.
368 * @param[in] extentions - The extensions of the firmware blob files.
369 */
370void setBiosAttr(const std::filesystem::path& elementsJsonFilePath,
371 const std::vector<std::string>& extensions)
372{
373 auto biosAttrStr = getBiosAttrStr(elementsJsonFilePath, extensions);
374
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000375 constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager";
376 constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager";
377 constexpr auto dbusAttrName = "hb_lid_ids";
378 constexpr auto dbusAttrType =
379 "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
380
381 using PendingAttributesType = std::vector<std::pair<
382 std::string, std::tuple<std::string, std::variant<std::string>>>>;
383 PendingAttributesType pendingAttributes;
384 pendingAttributes.emplace_back(std::make_pair(
385 dbusAttrName, std::make_tuple(dbusAttrType, biosAttrStr)));
386
387 auto bus = sdbusplus::bus::new_default();
388 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
389 MAPPER_INTERFACE, "GetObject");
390 method.append(biosConfigPath, std::vector<std::string>({biosConfigIntf}));
391 std::vector<std::pair<std::string, std::vector<std::string>>> response;
392 try
393 {
394 auto reply = bus.call(method);
395 reply.read(response);
396 if (response.empty())
397 {
398 log<level::ERR>("Error reading mapper response",
399 entry("PATH=%s", biosConfigPath),
400 entry("INTERFACE=%s", biosConfigIntf));
401 return;
402 }
403 auto method = bus.new_method_call((response.begin()->first).c_str(),
404 biosConfigPath,
405 SYSTEMD_PROPERTY_INTERFACE, "Set");
406 method.append(biosConfigIntf, "PendingAttributes",
407 std::variant<PendingAttributesType>(pendingAttributes));
408 bus.call(method);
409 }
Patrick Williams7b5685d2021-09-02 09:32:14 -0500410 catch (const sdbusplus::exception::exception& e)
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000411 {
412 log<level::ERR>("Error setting the bios attribute",
413 entry("ERROR=%s", e.what()),
414 entry("ATTRIBUTE=%s", dbusAttrName));
415 return;
416 }
417}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000418
419/**
Brad Bishop099543e2020-11-09 15:37:58 -0500420 * @brief Make callbacks on
421 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
422 *
423 * Look for an instance of
424 * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
425 * argument and if found, issue the provided callback.
426 *
427 * @param[in] interfacesAndProperties the interfaces in which to look for an
428 * instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem
429 * @param[in] callback the user callback to make if
430 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
431 * interfacesAndProperties
432 * @return true if interfacesAndProperties contained an instance of
433 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
434 */
435bool maybeCall(const std::map<std::string,
436 std::map<std::string,
437 std::variant<std::vector<std::string>>>>&
438 interfacesAndProperties,
439 const MaybeCallCallbackType& callback)
440{
441 using namespace std::string_literals;
442
443 static const auto interfaceName =
444 "xyz.openbmc_project.Configuration.IBMCompatibleSystem"s;
445 auto interfaceIterator = interfacesAndProperties.find(interfaceName);
446 if (interfaceIterator == interfacesAndProperties.cend())
447 {
448 // IBMCompatibleSystem interface not found, so instruct the caller to
449 // keep waiting or try again later.
450 return false;
451 }
452 auto propertyIterator = interfaceIterator->second.find("Names"s);
453 if (propertyIterator == interfaceIterator->second.cend())
454 {
455 // The interface exists but the property doesn't. This is a bug in the
456 // IBMCompatibleSystem implementation. The caller should not try
457 // again.
458 std::cerr << "Names property not implemented on " << interfaceName
459 << "\n";
460 return true;
461 }
462
463 const auto& ibmCompatibleSystem =
464 std::get<std::vector<std::string>>(propertyIterator->second);
465 if (callback)
466 {
467 callback(ibmCompatibleSystem);
468 }
469
470 // IBMCompatibleSystem found and callback issued.
471 return true;
472}
473
474/**
475 * @brief Make callbacks on
476 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
477 *
478 * Look for an instance of
479 * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
480 * argument and if found, issue the provided callback.
481 *
482 * @param[in] message the DBus message in which to look for an instance of
483 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
484 * @param[in] callback the user callback to make if
485 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
486 * message
487 * @return true if message contained an instance of
488 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
489 */
490bool maybeCallMessage(sdbusplus::message::message& message,
491 const MaybeCallCallbackType& callback)
492{
493 std::map<std::string,
494 std::map<std::string, std::variant<std::vector<std::string>>>>
495 interfacesAndProperties;
496 sdbusplus::message::object_path _;
497 message.read(_, interfacesAndProperties);
498 return maybeCall(interfacesAndProperties, callback);
499}
500
501/**
502 * @brief Determine system support for host firmware well-known names.
503 *
504 * Using the provided extensionMap and
505 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if
506 * well-known names for host firmare blob files are necessary and if so, create
507 * them.
508 *
509 * @param[in] extensionMap a map of
510 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
511 * file extensions.
512 * @param[in] hostFirmwareDirectory The directory in which findLinks should
513 * look for host firmware blob files that need well-known names.
514 * @param[in] ibmCompatibleSystem The names property of an instance of
515 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
516 * @param[in] errorCallback A callback made in the event of filesystem errors.
517 */
518void maybeMakeLinks(
519 const std::map<std::string, std::vector<std::string>>& extensionMap,
520 const std::filesystem::path& hostFirmwareDirectory,
521 const std::vector<std::string>& ibmCompatibleSystem,
522 const ErrorCallbackType& errorCallback)
523{
524 std::vector<std::string> extensions;
525 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
526 extensions))
527 {
528 findLinks(hostFirmwareDirectory, extensions, errorCallback, writeLink);
529 }
530}
531
532/**
Adriana Kobylak53a27392021-06-14 17:42:40 +0000533 * @brief Determine system support for updating the bios attribute table.
534 *
535 * Using the provided extensionMap and
536 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if the bios
537 * attribute table needs to be updated.
538 *
539 * @param[in] extensionMap a map of
540 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
541 * file extensions.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000542 * @param[in] elementsJsonFilePath The file path to the json file
Adriana Kobylak53a27392021-06-14 17:42:40 +0000543 * @param[in] ibmCompatibleSystem The names property of an instance of
544 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
545 */
546void maybeSetBiosAttr(
547 const std::map<std::string, std::vector<std::string>>& extensionMap,
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000548 const std::filesystem::path& elementsJsonFilePath,
Adriana Kobylak53a27392021-06-14 17:42:40 +0000549 const std::vector<std::string>& ibmCompatibleSystem)
550{
551 std::vector<std::string> extensions;
552 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
553 extensions))
554 {
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000555 setBiosAttr(elementsJsonFilePath, extensions);
Adriana Kobylak53a27392021-06-14 17:42:40 +0000556 }
557}
558
559/**
Brad Bishop099543e2020-11-09 15:37:58 -0500560 * @brief process host firmware
561 *
562 * Allocate a callback context and register for DBus.ObjectManager Interfaces
563 * added signals from entity manager.
564 *
565 * Check the current entity manager object tree for a
566 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instance (entity
567 * manager will be dbus activated if it is not running). If one is found,
568 * determine if symlinks need to be created and create them. Instruct the
569 * program event loop to exit.
570 *
571 * If no instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
572 * found return the callback context to main, where the program will sleep
573 * until the callback is invoked one or more times and instructs the program
574 * event loop to exit when
575 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is added.
576 *
577 * @param[in] bus a DBus client connection
578 * @param[in] extensionMap a map of
579 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
580 * file extensions.
581 * @param[in] hostFirmwareDirectory The directory in which processHostFirmware
582 * should look for blob files.
583 * @param[in] errorCallback A callback made in the event of filesystem errors.
584 * @param[in] loop a program event loop
585 * @return nullptr if an instance of
586 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found, otherwise a
587 * pointer to an sdbusplus match object.
588 */
589std::shared_ptr<void> processHostFirmware(
590 sdbusplus::bus::bus& bus,
591 std::map<std::string, std::vector<std::string>> extensionMap,
592 std::filesystem::path hostFirmwareDirectory,
593 ErrorCallbackType errorCallback, sdeventplus::Event& loop)
594{
595 // ownership of extensionMap, hostFirmwareDirectory and errorCallback can't
596 // be transfered to the match callback because they are needed in the non
597 // async part of this function below, so they need to be moved to the heap.
598 auto pExtensionMap =
599 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
600 auto pHostFirmwareDirectory =
601 std::make_shared<decltype(hostFirmwareDirectory)>(
602 std::move(hostFirmwareDirectory));
603 auto pErrorCallback =
604 std::make_shared<decltype(errorCallback)>(std::move(errorCallback));
605
606 // register for a callback in case the IBMCompatibleSystem interface has
607 // not yet been published by entity manager.
608 auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match::match>(
609 bus,
610 sdbusplus::bus::match::rules::interfacesAdded() +
611 sdbusplus::bus::match::rules::sender(
612 "xyz.openbmc_project.EntityManager"),
613 [pExtensionMap, pHostFirmwareDirectory, pErrorCallback,
614 &loop](auto& message) {
615 // bind the extension map, host firmware directory, and error
616 // callback to the maybeMakeLinks function.
617 auto maybeMakeLinksWithArgsBound =
618 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
619 std::cref(*pHostFirmwareDirectory),
620 std::placeholders::_1, std::cref(*pErrorCallback));
621
622 // if the InterfacesAdded message contains an an instance of
623 // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to
624 // see if links are necessary on this system and if so, create
625 // them.
626 if (maybeCallMessage(message, maybeMakeLinksWithArgsBound))
627 {
628 // The IBMCompatibleSystem interface was found and the links
629 // were created if applicable. Instruct the event loop /
630 // subcommand to exit.
631 loop.exit(0);
632 }
633 });
634
635 // now that we'll get a callback in the event of an InterfacesAdded signal
636 // (potentially containing
637 // xyz.openbmc_project.Configuration.IBMCompatibleSystem), activate entity
638 // manager if it isn't running and enumerate its objects
639 auto getManagedObjects = bus.new_method_call(
640 "xyz.openbmc_project.EntityManager", "/",
641 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Brad Bishop099543e2020-11-09 15:37:58 -0500642 std::map<std::string,
643 std::map<std::string, std::variant<std::vector<std::string>>>>
644 interfacesAndProperties;
645 std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)>
646 objects;
Adriana Kobylakc79fa912021-06-22 15:37:50 +0000647 try
648 {
649 auto reply = bus.call(getManagedObjects);
650 reply.read(objects);
651 }
Patrick Williams7b5685d2021-09-02 09:32:14 -0500652 catch (const sdbusplus::exception::exception& e)
Adriana Kobylakc79fa912021-06-22 15:37:50 +0000653 {
654 // Error querying the EntityManager interface. Return the match to have
655 // the callback run if/when the interface appears in D-Bus.
656 return interfacesAddedMatch;
657 }
Brad Bishop099543e2020-11-09 15:37:58 -0500658
659 // bind the extension map, host firmware directory, and error callback to
660 // the maybeMakeLinks function.
661 auto maybeMakeLinksWithArgsBound =
662 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
663 std::cref(*pHostFirmwareDirectory), std::placeholders::_1,
664 std::cref(*pErrorCallback));
665
666 for (const auto& pair : objects)
667 {
668 std::tie(std::ignore, interfacesAndProperties) = pair;
669 // if interfacesAndProperties contains an an instance of
670 // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to see
671 // if links are necessary on this system and if so, create them
672 if (maybeCall(interfacesAndProperties, maybeMakeLinksWithArgsBound))
673 {
674 // The IBMCompatibleSystem interface is already on the bus and the
675 // links were created if applicable. Instruct the event loop to
676 // exit.
677 loop.exit(0);
678 // The match object isn't needed anymore, so destroy it on return.
679 return nullptr;
680 }
681 }
682
683 // The IBMCompatibleSystem interface has not yet been published. Move
684 // ownership of the match callback to the caller.
685 return interfacesAddedMatch;
686}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000687
688/**
689 * @brief Update the Bios Attribute Table
690 *
691 * If an instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
692 * found, update the Bios Attribute Table with the appropriate host firmware
693 * data.
694 *
695 * @param[in] bus - D-Bus client connection.
696 * @param[in] extensionMap - Map of IBMCompatibleSystem names and host firmware
697 * file extensions.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000698 * @param[in] elementsJsonFilePath - The Path to the json file
Adriana Kobylak53a27392021-06-14 17:42:40 +0000699 * @param[in] loop - Program event loop.
700 * @return nullptr
701 */
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000702std::vector<std::shared_ptr<void>> updateBiosAttrTable(
Adriana Kobylak53a27392021-06-14 17:42:40 +0000703 sdbusplus::bus::bus& bus,
704 std::map<std::string, std::vector<std::string>> extensionMap,
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000705 std::filesystem::path elementsJsonFilePath, sdeventplus::Event& loop)
Adriana Kobylak53a27392021-06-14 17:42:40 +0000706{
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000707 constexpr auto pldmPath = "/xyz/openbmc_project/pldm";
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000708 constexpr auto entityManagerServiceName =
709 "xyz.openbmc_project.EntityManager";
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000710
Adriana Kobylak53a27392021-06-14 17:42:40 +0000711 auto pExtensionMap =
712 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000713 auto pElementsJsonFilePath =
714 std::make_shared<decltype(elementsJsonFilePath)>(
715 std::move(elementsJsonFilePath));
Adriana Kobylak53a27392021-06-14 17:42:40 +0000716
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000717 // Entity Manager is needed to get the list of supported extensions. Add a
718 // match to monitor interfaces added in case it's not running yet.
719 auto maybeSetAttrWithArgsBound =
720 std::bind(maybeSetBiosAttr, std::cref(*pExtensionMap),
721 std::cref(*pElementsJsonFilePath), std::placeholders::_1);
722
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000723 std::vector<std::shared_ptr<void>> matches;
724 matches.emplace_back(std::make_shared<sdbusplus::bus::match::match>(
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000725 bus,
726 sdbusplus::bus::match::rules::interfacesAdded() +
727 sdbusplus::bus::match::rules::sender(
728 "xyz.openbmc_project.EntityManager"),
729 [pldmPath, pExtensionMap, pElementsJsonFilePath,
730 maybeSetAttrWithArgsBound, &loop](auto& message) {
731 auto bus = sdbusplus::bus::new_default();
732 auto pldmObject = getObject(bus, pldmPath);
733 if (pldmObject.empty())
734 {
735 return;
736 }
737 if (maybeCallMessage(message, maybeSetAttrWithArgsBound))
738 {
739 loop.exit(0);
740 }
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000741 }));
742 matches.emplace_back(std::make_shared<sdbusplus::bus::match::match>(
743 bus,
744 sdbusplus::bus::match::rules::nameOwnerChanged() +
745 sdbusplus::bus::match::rules::arg0namespace(
746 "xyz.openbmc_project.PLDM"),
747 [pExtensionMap, pElementsJsonFilePath, maybeSetAttrWithArgsBound,
748 &loop](auto& message) {
749 std::string name;
750 std::string oldOwner;
751 std::string newOwner;
752 message.read(name, oldOwner, newOwner);
753
754 if (newOwner.empty())
755 {
756 return;
757 }
758
759 auto bus = sdbusplus::bus::new_default();
760 InterfacesPropertiesMap interfacesAndProperties;
761 auto objects = getManagedObjects(bus, entityManagerServiceName);
762 for (const auto& pair : objects)
763 {
764 std::tie(std::ignore, interfacesAndProperties) = pair;
765 if (maybeCall(interfacesAndProperties,
766 maybeSetAttrWithArgsBound))
767 {
768 loop.exit(0);
769 }
770 }
771 }));
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000772
Adriana Kobylakfd4a6082021-06-21 15:53:34 +0000773 // The BIOS attribute table can only be updated if PLDM is running because
774 // PLDM is the one that exposes this property. Return if it's not running.
Adriana Kobylakfd4a6082021-06-21 15:53:34 +0000775 auto pldmObject = getObject(bus, pldmPath);
776 if (pldmObject.empty())
777 {
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000778 return matches;
Adriana Kobylakfd4a6082021-06-21 15:53:34 +0000779 }
780
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000781 InterfacesPropertiesMap interfacesAndProperties;
782 auto objects = getManagedObjects(bus, entityManagerServiceName);
Adriana Kobylak53a27392021-06-14 17:42:40 +0000783 for (const auto& pair : objects)
784 {
785 std::tie(std::ignore, interfacesAndProperties) = pair;
786 if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound))
787 {
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000788 loop.exit(0);
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000789 return {};
Adriana Kobylak53a27392021-06-14 17:42:40 +0000790 }
791 }
792
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000793 return matches;
Adriana Kobylak53a27392021-06-14 17:42:40 +0000794}
795
Brad Bishop099543e2020-11-09 15:37:58 -0500796} // namespace process_hostfirmware
797} // namespace functions