blob: fe112a40f75a14d427e63caf69df70e02e3ac4cc [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>
Adriana Kobylak2b78eb02022-04-06 19:38:47 +000016#include <xyz/openbmc_project/Common/error.hpp>
Brad Bishop099543e2020-11-09 15:37:58 -050017
18#include <filesystem>
Adriana Kobylakae0998f2021-06-16 19:52:24 +000019#include <fstream>
Brad Bishop099543e2020-11-09 15:37:58 -050020#include <functional>
21#include <iostream>
22#include <map>
23#include <memory>
24#include <string>
25#include <variant>
26#include <vector>
27
28namespace functions
29{
30namespace process_hostfirmware
31{
32
Adriana Kobylak56f538c2021-06-16 20:37:21 +000033using namespace phosphor::logging;
Adriana Kobylakebf67bf2021-06-21 18:38:17 +000034using InterfacesPropertiesMap =
35 std::map<std::string,
36 std::map<std::string, std::variant<std::vector<std::string>>>>;
37using ManagedObjectType =
38 std::map<sdbusplus::message::object_path, InterfacesPropertiesMap>;
Adriana Kobylak56f538c2021-06-16 20:37:21 +000039
Brad Bishop099543e2020-11-09 15:37:58 -050040/**
Adriana Kobylakebf67bf2021-06-21 18:38:17 +000041 * @brief Returns the managed objects for a given service
42 */
43ManagedObjectType getManagedObjects(sdbusplus::bus::bus& bus,
44 const std::string& service)
45{
46 auto method = bus.new_method_call(service.c_str(), "/",
47 "org.freedesktop.DBus.ObjectManager",
48 "GetManagedObjects");
49
50 ManagedObjectType objects;
51
52 try
53 {
54 auto reply = bus.call(method);
55 reply.read(objects);
56 }
Patrick Williams7b5685d2021-09-02 09:32:14 -050057 catch (const sdbusplus::exception::exception& e)
Adriana Kobylakebf67bf2021-06-21 18:38:17 +000058 {
59 return ManagedObjectType{};
60 }
61 return objects;
62}
63
64/**
Brad Bishop099543e2020-11-09 15:37:58 -050065 * @brief Issue callbacks safely
66 *
67 * std::function can be empty, so this wrapper method checks for that prior to
68 * calling it to avoid std::bad_function_call
69 *
70 * @tparam Sig the types of the std::function arguments
71 * @tparam Args the deduced argument types
72 * @param[in] callback the callback being wrapped
73 * @param[in] args the callback arguments
74 */
75template <typename... Sig, typename... Args>
76void makeCallback(const std::function<void(Sig...)>& callback, Args&&... args)
77{
78 if (callback)
79 {
80 callback(std::forward<Args>(args)...);
81 }
82}
83
84/**
85 * @brief Get file extensions for IBMCompatibleSystem
86 *
87 * IBM host firmware can be deployed as blobs (files) in a filesystem. Host
88 * firmware blobs for different values of
89 * xyz.openbmc_project.Configuration.IBMCompatibleSystem are packaged with
90 * different filename extensions. getExtensionsForIbmCompatibleSystem
91 * maintains the mapping from a given value of
92 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to an array of
93 * filename extensions.
94 *
95 * If a mapping is found getExtensionsForIbmCompatibleSystem returns true and
96 * the extensions parameter is reset with the map entry. If no mapping is
97 * found getExtensionsForIbmCompatibleSystem returns false and extensions is
98 * unmodified.
99 *
100 * @param[in] extensionMap a map of
101 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
102 * file extensions.
103 * @param[in] ibmCompatibleSystem The names property of an instance of
104 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
105 * @param[out] extentions the host firmware blob file extensions
106 * @return true if an entry was found, otherwise false
107 */
108bool getExtensionsForIbmCompatibleSystem(
109 const std::map<std::string, std::vector<std::string>>& extensionMap,
110 const std::vector<std::string>& ibmCompatibleSystem,
111 std::vector<std::string>& extensions)
112{
113 for (const auto& system : ibmCompatibleSystem)
114 {
115 auto extensionMapIterator = extensionMap.find(system);
116 if (extensionMapIterator != extensionMap.end())
117 {
118 extensions = extensionMapIterator->second;
119 return true;
120 }
121 }
122
123 return false;
124}
125
126/**
127 * @brief Write host firmware well-known name
128 *
129 * A wrapper around std::filesystem::create_symlink that avoids EEXIST by
130 * deleting any pre-existing file.
131 *
132 * @param[in] linkTarget The link target argument to
133 * std::filesystem::create_symlink
134 * @param[in] linkPath The link path argument to std::filesystem::create_symlink
135 * @param[in] errorCallback A callback made in the event of filesystem errors.
136 */
137void writeLink(const std::filesystem::path& linkTarget,
138 const std::filesystem::path& linkPath,
139 const ErrorCallbackType& errorCallback)
140{
141 std::error_code ec;
142
143 // remove files with the same name as the symlink to be created,
144 // otherwise symlink will fail with EEXIST.
145 if (!std::filesystem::remove(linkPath, ec))
146 {
147 if (ec)
148 {
149 makeCallback(errorCallback, linkPath, ec);
150 return;
151 }
152 }
153
154 std::filesystem::create_symlink(linkTarget, linkPath, ec);
155 if (ec)
156 {
157 makeCallback(errorCallback, linkPath, ec);
158 return;
159 }
160}
161
162/**
163 * @brief Find host firmware blob files that need well-known names
164 *
165 * The IBM host firmware runtime looks for data and/or additional code while
166 * bootstraping in files with well-known names. findLinks uses the provided
167 * extensions argument to find host firmware blob files that require a
168 * well-known name. When a blob is found, issue the provided callback
169 * (typically a function that will write a symlink).
170 *
171 * @param[in] hostFirmwareDirectory The directory in which findLinks should
172 * look for host firmware blob files that need well-known names.
173 * @param[in] extentions The extensions of the firmware blob files denote a
174 * host firmware blob file requires a well-known name.
175 * @param[in] errorCallback A callback made in the event of filesystem errors.
176 * @param[in] linkCallback A callback made when host firmware blob files
177 * needing a well known name are found.
178 */
179void findLinks(const std::filesystem::path& hostFirmwareDirectory,
180 const std::vector<std::string>& extensions,
181 const ErrorCallbackType& errorCallback,
182 const LinkCallbackType& linkCallback)
183{
184 std::error_code ec;
185 std::filesystem::directory_iterator directoryIterator(hostFirmwareDirectory,
186 ec);
187 if (ec)
188 {
189 makeCallback(errorCallback, hostFirmwareDirectory, ec);
190 return;
191 }
192
Adriana Kobylak4e82bc82021-09-28 19:25:39 +0000193 // Create a symlink for pnor.toc
194 static const auto tocLid = "81e00994.lid";
195 auto tocLidPath = hostFirmwareDirectory / tocLid;
196 if (std::filesystem::exists(tocLidPath))
197 {
198 static const auto tocName = "pnor.toc";
199 auto tocLinkPath = hostFirmwareDirectory / tocName;
200 makeCallback(linkCallback, tocLid, tocLinkPath, errorCallback);
201 }
202
Brad Bishop099543e2020-11-09 15:37:58 -0500203 for (; directoryIterator != std::filesystem::end(directoryIterator);
204 directoryIterator.increment(ec))
205 {
206 const auto& file = directoryIterator->path();
207 if (ec)
208 {
209 makeCallback(errorCallback, file, ec);
210 // quit here if the increment call failed otherwise the loop may
211 // never finish
212 break;
213 }
214
215 if (std::find(extensions.begin(), extensions.end(), file.extension()) ==
216 extensions.end())
217 {
218 // this file doesn't have an extension or doesn't match any of the
219 // provided extensions.
220 continue;
221 }
222
223 auto linkPath(file.parent_path().append(
224 static_cast<const std::string&>(file.stem())));
225
226 makeCallback(linkCallback, file.filename(), linkPath, errorCallback);
227 }
228}
229
230/**
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000231 * @brief Parse the elements json file and construct a string with the data to
232 * be used to update the bios attribute table.
233 *
234 * @param[in] elementsJsonFilePath - The path to the host firmware json file.
235 * @param[in] extensions - The extensions of the firmware blob files.
Adriana Kobylak53a27392021-06-14 17:42:40 +0000236 */
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000237std::string getBiosAttrStr(const std::filesystem::path& elementsJsonFilePath,
238 const std::vector<std::string>& extensions)
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000239{
240 std::string biosAttrStr{};
241
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000242 std::ifstream jsonFile(elementsJsonFilePath.c_str());
243 if (!jsonFile)
244 {
245 return {};
246 }
247
248 std::map<std::string, std::string> attr;
249 auto data = nlohmann::json::parse(jsonFile, nullptr, false);
250 if (data.is_discarded())
251 {
252 log<level::ERR>("Error parsing JSON file",
253 entry("FILE=%s", elementsJsonFilePath.c_str()));
254 return {};
255 }
256
257 // .get requires a non-const iterator
258 for (auto& iter : data["lids"])
259 {
260 std::string name{};
261 std::string lid{};
262
263 try
264 {
265 name = iter["element_name"].get<std::string>();
266 lid = iter["short_lid_name"].get<std::string>();
267 }
Patrick Williams97a709b2021-10-06 13:05:40 -0500268 catch (const std::exception& e)
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000269 {
270 // Possibly the element or lid name field was not found
271 log<level::ERR>("Error reading JSON field",
272 entry("FILE=%s", elementsJsonFilePath.c_str()),
273 entry("ERROR=%s", e.what()));
274 continue;
275 }
276
277 // The elements with the ipl extension have higher priority. Therefore
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000278 // Use operator[] to overwrite value if an entry for it already exists,
279 // and create a second entry with key name element_RT to specify it as
280 // a runtime element.
281 // Ex: if the JSON contains an entry A.P10 with lid name X, it'll create
282 // and try A=X. If the JSON also contained an entry A.P10.iplTime with
283 // lid name Y, the A entry would be overwritten to be A=Y and a second
284 // entry A_RT=X would be created.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000285 constexpr auto iplExtension = ".iplTime";
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000286 constexpr auto runtimeSuffix = "_RT";
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000287 std::filesystem::path path(name);
288 if (path.extension() == iplExtension)
289 {
290 // Some elements have an additional extension, ex: .P10.iplTime
291 // Strip off the ipl extension with stem(), then check if there is
292 // an additional extension with extension().
293 if (!path.stem().extension().empty())
294 {
295 // Check if the extension matches the extensions for this system
296 if (std::find(extensions.begin(), extensions.end(),
297 path.stem().extension()) == extensions.end())
298 {
299 continue;
300 }
301 }
302 // Get the element name without extensions by calling stem() twice
303 // since stem() returns the base name if no periods are found.
304 // Therefore both "element.P10" and "element.P10.iplTime" would
305 // become "element".
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000306 auto keyName = path.stem().stem();
307 auto attrIt = attr.find(keyName);
308 if (attrIt != attr.end())
309 {
310 // Copy the existing entry to a runtime entry
311 auto runtimeKeyName = keyName.string() + runtimeSuffix;
312 attr.insert({runtimeKeyName, attrIt->second});
313 }
314 // Overwrite the exsiting element with the ipl entry
315 attr[keyName] = lid;
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000316 continue;
317 }
318
319 // Process all other extensions. The extension should match the list of
320 // supported extensions for this system. Use .insert() to only add
321 // entries that do not exist, so to not overwrite the values that may
322 // had been added that had the ipl extension.
323 if (std::find(extensions.begin(), extensions.end(), path.extension()) !=
324 extensions.end())
325 {
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000326 auto keyName = path.stem();
327 auto attrIt = attr.find(keyName);
328 if (attrIt != attr.end())
329 {
330 // The existing entry is an ipl entry, therefore create this
331 // entry as a runtime one.
332 auto runtimeKeyName = keyName.string() + runtimeSuffix;
333 attr.insert({runtimeKeyName, lid});
334 }
335 else
336 {
337 attr.insert({path.stem(), lid});
338 }
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000339 }
340 }
341 for (const auto& a : attr)
342 {
343 // Build the bios attribute string with format:
344 // "element1=lid1,element2=lid2,elementN=lidN,"
345 biosAttrStr += a.first + "=" + a.second + ",";
Adriana Kobylak5dc5d6c2021-07-20 20:21:16 +0000346
347 // Create symlinks from the hostfw elements to their corresponding
348 // lid files if they don't exist
349 auto elementFilePath =
350 std::filesystem::path("/media/hostfw/running") / a.first;
351 if (!std::filesystem::exists(elementFilePath))
352 {
353 std::error_code ec;
354 auto lidName = a.second + ".lid";
355 std::filesystem::create_symlink(lidName, elementFilePath, ec);
356 if (ec)
357 {
358 log<level::ERR>("Error creating symlink",
359 entry("TARGET=%s", lidName.c_str()),
360 entry("LINK=%s", elementFilePath.c_str()));
361 }
362 }
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000363 }
364
George Liua38f6e62021-09-20 08:53:00 +0800365 // Delete the last comma of the bios attribute string
366 if (biosAttrStr.back() == ',')
367 {
368 return biosAttrStr.substr(0, biosAttrStr.length() - 1);
369 }
370
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000371 return biosAttrStr;
372}
373
374/**
375 * @brief Set the bios attribute table with details of the host firmware data
376 * for this system.
377 *
378 * @param[in] elementsJsonFilePath - The path to the host firmware json file.
379 * @param[in] extentions - The extensions of the firmware blob files.
380 */
381void setBiosAttr(const std::filesystem::path& elementsJsonFilePath,
382 const std::vector<std::string>& extensions)
383{
384 auto biosAttrStr = getBiosAttrStr(elementsJsonFilePath, extensions);
385
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000386 constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager";
387 constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager";
388 constexpr auto dbusAttrName = "hb_lid_ids";
389 constexpr auto dbusAttrType =
390 "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
391
392 using PendingAttributesType = std::vector<std::pair<
393 std::string, std::tuple<std::string, std::variant<std::string>>>>;
394 PendingAttributesType pendingAttributes;
395 pendingAttributes.emplace_back(std::make_pair(
396 dbusAttrName, std::make_tuple(dbusAttrType, biosAttrStr)));
397
398 auto bus = sdbusplus::bus::new_default();
399 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
400 MAPPER_INTERFACE, "GetObject");
401 method.append(biosConfigPath, std::vector<std::string>({biosConfigIntf}));
402 std::vector<std::pair<std::string, std::vector<std::string>>> response;
403 try
404 {
405 auto reply = bus.call(method);
406 reply.read(response);
407 if (response.empty())
408 {
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000409 log<level::INFO>("Error reading mapper response",
410 entry("PATH=%s", biosConfigPath),
411 entry("INTERFACE=%s", biosConfigIntf));
412 throw sdbusplus::xyz::openbmc_project::Common::Error::
413 InternalFailure();
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000414 }
415 auto method = bus.new_method_call((response.begin()->first).c_str(),
416 biosConfigPath,
417 SYSTEMD_PROPERTY_INTERFACE, "Set");
418 method.append(biosConfigIntf, "PendingAttributes",
419 std::variant<PendingAttributesType>(pendingAttributes));
420 bus.call(method);
421 }
Patrick Williams7b5685d2021-09-02 09:32:14 -0500422 catch (const sdbusplus::exception::exception& e)
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000423 {
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000424 log<level::INFO>("Error setting the bios attribute",
425 entry("ERROR=%s", e.what()),
426 entry("ATTRIBUTE=%s", dbusAttrName));
427 throw;
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000428 }
429}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000430
431/**
Brad Bishop099543e2020-11-09 15:37:58 -0500432 * @brief Make callbacks on
433 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
434 *
435 * Look for an instance of
436 * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
437 * argument and if found, issue the provided callback.
438 *
439 * @param[in] interfacesAndProperties the interfaces in which to look for an
440 * instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem
441 * @param[in] callback the user callback to make if
442 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
443 * interfacesAndProperties
444 * @return true if interfacesAndProperties contained an instance of
445 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
446 */
447bool maybeCall(const std::map<std::string,
448 std::map<std::string,
449 std::variant<std::vector<std::string>>>>&
450 interfacesAndProperties,
451 const MaybeCallCallbackType& callback)
452{
453 using namespace std::string_literals;
454
455 static const auto interfaceName =
456 "xyz.openbmc_project.Configuration.IBMCompatibleSystem"s;
457 auto interfaceIterator = interfacesAndProperties.find(interfaceName);
458 if (interfaceIterator == interfacesAndProperties.cend())
459 {
460 // IBMCompatibleSystem interface not found, so instruct the caller to
461 // keep waiting or try again later.
462 return false;
463 }
464 auto propertyIterator = interfaceIterator->second.find("Names"s);
465 if (propertyIterator == interfaceIterator->second.cend())
466 {
467 // The interface exists but the property doesn't. This is a bug in the
468 // IBMCompatibleSystem implementation. The caller should not try
469 // again.
470 std::cerr << "Names property not implemented on " << interfaceName
471 << "\n";
472 return true;
473 }
474
475 const auto& ibmCompatibleSystem =
476 std::get<std::vector<std::string>>(propertyIterator->second);
477 if (callback)
478 {
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000479 try
480 {
481 callback(ibmCompatibleSystem);
482 }
483 catch (const sdbusplus::exception::exception& e)
484 {
485 return false;
486 }
Brad Bishop099543e2020-11-09 15:37:58 -0500487 }
488
489 // IBMCompatibleSystem found and callback issued.
490 return true;
491}
492
493/**
494 * @brief Make callbacks on
495 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances.
496 *
497 * Look for an instance of
498 * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided
499 * argument and if found, issue the provided callback.
500 *
501 * @param[in] message the DBus message in which to look for an instance of
502 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
503 * @param[in] callback the user callback to make if
504 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in
505 * message
506 * @return true if message contained an instance of
507 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise
508 */
509bool maybeCallMessage(sdbusplus::message::message& message,
510 const MaybeCallCallbackType& callback)
511{
512 std::map<std::string,
513 std::map<std::string, std::variant<std::vector<std::string>>>>
514 interfacesAndProperties;
515 sdbusplus::message::object_path _;
516 message.read(_, interfacesAndProperties);
517 return maybeCall(interfacesAndProperties, callback);
518}
519
520/**
521 * @brief Determine system support for host firmware well-known names.
522 *
523 * Using the provided extensionMap and
524 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if
525 * well-known names for host firmare blob files are necessary and if so, create
526 * them.
527 *
528 * @param[in] extensionMap a map of
529 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
530 * file extensions.
531 * @param[in] hostFirmwareDirectory The directory in which findLinks should
532 * look for host firmware blob files that need well-known names.
533 * @param[in] ibmCompatibleSystem The names property of an instance of
534 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
535 * @param[in] errorCallback A callback made in the event of filesystem errors.
536 */
537void maybeMakeLinks(
538 const std::map<std::string, std::vector<std::string>>& extensionMap,
539 const std::filesystem::path& hostFirmwareDirectory,
540 const std::vector<std::string>& ibmCompatibleSystem,
541 const ErrorCallbackType& errorCallback)
542{
543 std::vector<std::string> extensions;
544 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
545 extensions))
546 {
547 findLinks(hostFirmwareDirectory, extensions, errorCallback, writeLink);
548 }
549}
550
551/**
Adriana Kobylak53a27392021-06-14 17:42:40 +0000552 * @brief Determine system support for updating the bios attribute table.
553 *
554 * Using the provided extensionMap and
555 * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if the bios
556 * attribute table needs to be updated.
557 *
558 * @param[in] extensionMap a map of
559 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
560 * file extensions.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000561 * @param[in] elementsJsonFilePath The file path to the json file
Adriana Kobylak53a27392021-06-14 17:42:40 +0000562 * @param[in] ibmCompatibleSystem The names property of an instance of
563 * xyz.openbmc_project.Configuration.IBMCompatibleSystem
564 */
565void maybeSetBiosAttr(
566 const std::map<std::string, std::vector<std::string>>& extensionMap,
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000567 const std::filesystem::path& elementsJsonFilePath,
Adriana Kobylak53a27392021-06-14 17:42:40 +0000568 const std::vector<std::string>& ibmCompatibleSystem)
569{
570 std::vector<std::string> extensions;
571 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
572 extensions))
573 {
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000574 try
575 {
576 setBiosAttr(elementsJsonFilePath, extensions);
577 }
578 catch (const sdbusplus::exception::exception& e)
579 {
580 throw;
581 }
Adriana Kobylak53a27392021-06-14 17:42:40 +0000582 }
583}
584
585/**
Brad Bishop099543e2020-11-09 15:37:58 -0500586 * @brief process host firmware
587 *
588 * Allocate a callback context and register for DBus.ObjectManager Interfaces
589 * added signals from entity manager.
590 *
591 * Check the current entity manager object tree for a
592 * xyz.openbmc_project.Configuration.IBMCompatibleSystem instance (entity
593 * manager will be dbus activated if it is not running). If one is found,
594 * determine if symlinks need to be created and create them. Instruct the
595 * program event loop to exit.
596 *
597 * If no instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
598 * found return the callback context to main, where the program will sleep
599 * until the callback is invoked one or more times and instructs the program
600 * event loop to exit when
601 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is added.
602 *
603 * @param[in] bus a DBus client connection
604 * @param[in] extensionMap a map of
605 * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob
606 * file extensions.
607 * @param[in] hostFirmwareDirectory The directory in which processHostFirmware
608 * should look for blob files.
609 * @param[in] errorCallback A callback made in the event of filesystem errors.
610 * @param[in] loop a program event loop
611 * @return nullptr if an instance of
612 * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found, otherwise a
613 * pointer to an sdbusplus match object.
614 */
615std::shared_ptr<void> processHostFirmware(
616 sdbusplus::bus::bus& bus,
617 std::map<std::string, std::vector<std::string>> extensionMap,
618 std::filesystem::path hostFirmwareDirectory,
619 ErrorCallbackType errorCallback, sdeventplus::Event& loop)
620{
621 // ownership of extensionMap, hostFirmwareDirectory and errorCallback can't
622 // be transfered to the match callback because they are needed in the non
623 // async part of this function below, so they need to be moved to the heap.
624 auto pExtensionMap =
625 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
626 auto pHostFirmwareDirectory =
627 std::make_shared<decltype(hostFirmwareDirectory)>(
628 std::move(hostFirmwareDirectory));
629 auto pErrorCallback =
630 std::make_shared<decltype(errorCallback)>(std::move(errorCallback));
631
632 // register for a callback in case the IBMCompatibleSystem interface has
633 // not yet been published by entity manager.
634 auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match::match>(
635 bus,
636 sdbusplus::bus::match::rules::interfacesAdded() +
637 sdbusplus::bus::match::rules::sender(
638 "xyz.openbmc_project.EntityManager"),
639 [pExtensionMap, pHostFirmwareDirectory, pErrorCallback,
640 &loop](auto& message) {
641 // bind the extension map, host firmware directory, and error
642 // callback to the maybeMakeLinks function.
643 auto maybeMakeLinksWithArgsBound =
644 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
645 std::cref(*pHostFirmwareDirectory),
646 std::placeholders::_1, std::cref(*pErrorCallback));
647
648 // if the InterfacesAdded message contains an an instance of
649 // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to
650 // see if links are necessary on this system and if so, create
651 // them.
652 if (maybeCallMessage(message, maybeMakeLinksWithArgsBound))
653 {
654 // The IBMCompatibleSystem interface was found and the links
655 // were created if applicable. Instruct the event loop /
656 // subcommand to exit.
657 loop.exit(0);
658 }
659 });
660
661 // now that we'll get a callback in the event of an InterfacesAdded signal
662 // (potentially containing
663 // xyz.openbmc_project.Configuration.IBMCompatibleSystem), activate entity
664 // manager if it isn't running and enumerate its objects
665 auto getManagedObjects = bus.new_method_call(
666 "xyz.openbmc_project.EntityManager", "/",
667 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Brad Bishop099543e2020-11-09 15:37:58 -0500668 std::map<std::string,
669 std::map<std::string, std::variant<std::vector<std::string>>>>
670 interfacesAndProperties;
671 std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)>
672 objects;
Adriana Kobylakc79fa912021-06-22 15:37:50 +0000673 try
674 {
675 auto reply = bus.call(getManagedObjects);
676 reply.read(objects);
677 }
Patrick Williams7b5685d2021-09-02 09:32:14 -0500678 catch (const sdbusplus::exception::exception& e)
Adriana Kobylakc79fa912021-06-22 15:37:50 +0000679 {
680 // Error querying the EntityManager interface. Return the match to have
681 // the callback run if/when the interface appears in D-Bus.
682 return interfacesAddedMatch;
683 }
Brad Bishop099543e2020-11-09 15:37:58 -0500684
685 // bind the extension map, host firmware directory, and error callback to
686 // the maybeMakeLinks function.
687 auto maybeMakeLinksWithArgsBound =
688 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
689 std::cref(*pHostFirmwareDirectory), std::placeholders::_1,
690 std::cref(*pErrorCallback));
691
692 for (const auto& pair : objects)
693 {
694 std::tie(std::ignore, interfacesAndProperties) = pair;
695 // if interfacesAndProperties contains an an instance of
696 // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to see
697 // if links are necessary on this system and if so, create them
698 if (maybeCall(interfacesAndProperties, maybeMakeLinksWithArgsBound))
699 {
700 // The IBMCompatibleSystem interface is already on the bus and the
701 // links were created if applicable. Instruct the event loop to
702 // exit.
703 loop.exit(0);
704 // The match object isn't needed anymore, so destroy it on return.
705 return nullptr;
706 }
707 }
708
709 // The IBMCompatibleSystem interface has not yet been published. Move
710 // ownership of the match callback to the caller.
711 return interfacesAddedMatch;
712}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000713
714/**
715 * @brief Update the Bios Attribute Table
716 *
717 * If an instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is
718 * found, update the Bios Attribute Table with the appropriate host firmware
719 * data.
720 *
721 * @param[in] bus - D-Bus client connection.
722 * @param[in] extensionMap - Map of IBMCompatibleSystem names and host firmware
723 * file extensions.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000724 * @param[in] elementsJsonFilePath - The Path to the json file
Adriana Kobylak53a27392021-06-14 17:42:40 +0000725 * @param[in] loop - Program event loop.
726 * @return nullptr
727 */
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000728std::vector<std::shared_ptr<void>> updateBiosAttrTable(
Adriana Kobylak53a27392021-06-14 17:42:40 +0000729 sdbusplus::bus::bus& bus,
730 std::map<std::string, std::vector<std::string>> extensionMap,
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000731 std::filesystem::path elementsJsonFilePath, sdeventplus::Event& loop)
Adriana Kobylak53a27392021-06-14 17:42:40 +0000732{
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000733 constexpr auto pldmPath = "/xyz/openbmc_project/pldm";
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000734 constexpr auto entityManagerServiceName =
735 "xyz.openbmc_project.EntityManager";
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000736
Adriana Kobylak53a27392021-06-14 17:42:40 +0000737 auto pExtensionMap =
738 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000739 auto pElementsJsonFilePath =
740 std::make_shared<decltype(elementsJsonFilePath)>(
741 std::move(elementsJsonFilePath));
Adriana Kobylak53a27392021-06-14 17:42:40 +0000742
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000743 auto maybeSetAttrWithArgsBound =
744 std::bind(maybeSetBiosAttr, std::cref(*pExtensionMap),
745 std::cref(*pElementsJsonFilePath), std::placeholders::_1);
746
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000747 std::vector<std::shared_ptr<void>> matches;
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000748
749 // Entity Manager is needed to get the list of supported extensions. Add a
750 // match to monitor interfaces added in case it's not running yet.
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000751 matches.emplace_back(std::make_shared<sdbusplus::bus::match::match>(
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000752 bus,
753 sdbusplus::bus::match::rules::interfacesAdded() +
754 sdbusplus::bus::match::rules::sender(
755 "xyz.openbmc_project.EntityManager"),
756 [pldmPath, pExtensionMap, pElementsJsonFilePath,
757 maybeSetAttrWithArgsBound, &loop](auto& message) {
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000758 if (maybeCallMessage(message, maybeSetAttrWithArgsBound))
759 {
760 loop.exit(0);
761 }
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000762 }));
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000763
764 // The BIOS attribute table can only be updated if PLDM is running because
765 // PLDM is the one that exposes this property. Add a match to monitor when
766 // the PLDM service starts.
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000767 matches.emplace_back(std::make_shared<sdbusplus::bus::match::match>(
768 bus,
769 sdbusplus::bus::match::rules::nameOwnerChanged() +
770 sdbusplus::bus::match::rules::arg0namespace(
771 "xyz.openbmc_project.PLDM"),
772 [pExtensionMap, pElementsJsonFilePath, maybeSetAttrWithArgsBound,
773 &loop](auto& message) {
774 std::string name;
775 std::string oldOwner;
776 std::string newOwner;
777 message.read(name, oldOwner, newOwner);
778
779 if (newOwner.empty())
780 {
781 return;
782 }
783
784 auto bus = sdbusplus::bus::new_default();
785 InterfacesPropertiesMap interfacesAndProperties;
786 auto objects = getManagedObjects(bus, entityManagerServiceName);
787 for (const auto& pair : objects)
788 {
789 std::tie(std::ignore, interfacesAndProperties) = pair;
790 if (maybeCall(interfacesAndProperties,
791 maybeSetAttrWithArgsBound))
792 {
793 loop.exit(0);
794 }
795 }
796 }));
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000797
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000798 InterfacesPropertiesMap interfacesAndProperties;
799 auto objects = getManagedObjects(bus, entityManagerServiceName);
Adriana Kobylak53a27392021-06-14 17:42:40 +0000800 for (const auto& pair : objects)
801 {
802 std::tie(std::ignore, interfacesAndProperties) = pair;
803 if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound))
804 {
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000805 loop.exit(0);
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000806 return {};
Adriana Kobylak53a27392021-06-14 17:42:40 +0000807 }
808 }
809
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000810 return matches;
Adriana Kobylak53a27392021-06-14 17:42:40 +0000811}
812
Brad Bishop099543e2020-11-09 15:37:58 -0500813} // namespace process_hostfirmware
814} // namespace functions