blob: 0d2b34c46f06a39078b3cea3eb63987b67ba74c8 [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
Adriana Kobylakf3b0c282024-10-15 14:14:59 -050040constexpr auto tocName = "pnor.toc";
41
Brad Bishop099543e2020-11-09 15:37:58 -050042/**
Adriana Kobylakebf67bf2021-06-21 18:38:17 +000043 * @brief Returns the managed objects for a given service
44 */
Patrick Williams0dea1992022-07-22 19:26:52 -050045ManagedObjectType getManagedObjects(sdbusplus::bus_t& bus,
Nan Zhou75352b42022-09-20 20:16:19 +000046 const std::string& service,
47 const std::string& managerPath)
48
Adriana Kobylakebf67bf2021-06-21 18:38:17 +000049{
Nan Zhou75352b42022-09-20 20:16:19 +000050 auto method = bus.new_method_call(service.c_str(), managerPath.c_str(),
Adriana Kobylakebf67bf2021-06-21 18:38:17 +000051 "org.freedesktop.DBus.ObjectManager",
52 "GetManagedObjects");
53
54 ManagedObjectType objects;
55
56 try
57 {
58 auto reply = bus.call(method);
59 reply.read(objects);
60 }
Patrick Williams0dea1992022-07-22 19:26:52 -050061 catch (const sdbusplus::exception_t& e)
Adriana Kobylakebf67bf2021-06-21 18:38:17 +000062 {
63 return ManagedObjectType{};
64 }
65 return objects;
66}
67
68/**
Brad Bishop099543e2020-11-09 15:37:58 -050069 * @brief Issue callbacks safely
70 *
71 * std::function can be empty, so this wrapper method checks for that prior to
72 * calling it to avoid std::bad_function_call
73 *
74 * @tparam Sig the types of the std::function arguments
75 * @tparam Args the deduced argument types
76 * @param[in] callback the callback being wrapped
77 * @param[in] args the callback arguments
78 */
79template <typename... Sig, typename... Args>
80void makeCallback(const std::function<void(Sig...)>& callback, Args&&... args)
81{
82 if (callback)
83 {
84 callback(std::forward<Args>(args)...);
85 }
86}
87
88/**
Adriana Kobylak749bdcc2024-01-17 12:56:00 -060089 * @brief Get file extensions for Compatible
Brad Bishop099543e2020-11-09 15:37:58 -050090 *
Adriana Kobylak749bdcc2024-01-17 12:56:00 -060091 * IBM host firmware can be deployed as blobs (files) in a filesystem. Host
Brad Bishop099543e2020-11-09 15:37:58 -050092 * firmware blobs for different values of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -060093 * xyz.openbmc_project.Inventory.Decorator.Compatible are packaged with
94 * different filename extensions. getExtensionsForIbmCompatibleSystem maintains
95 * the mapping from a given value of
96 * xyz.openbmc_project.Inventory.Decorator.Compatible to an array of filename
97 * extensions.
Brad Bishop099543e2020-11-09 15:37:58 -050098 *
99 * If a mapping is found getExtensionsForIbmCompatibleSystem returns true and
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600100 * the extensions parameter is reset with the map entry. If no mapping is found
101 * getExtensionsForIbmCompatibleSystem returns false and extensions is
Brad Bishop099543e2020-11-09 15:37:58 -0500102 * unmodified.
103 *
104 * @param[in] extensionMap a map of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600105 * xyz.openbmc_project.Inventory.Decorator.Compatible to host firmware blob file
106 * extensions.
Brad Bishop099543e2020-11-09 15:37:58 -0500107 * @param[in] ibmCompatibleSystem The names property of an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600108 * xyz.openbmc_project.Inventory.Decorator.Compatible
Manojkiran Eda96442c82024-06-17 10:24:05 +0530109 * @param[out] extensions the host firmware blob file extensions
Brad Bishop099543e2020-11-09 15:37:58 -0500110 * @return true if an entry was found, otherwise false
111 */
112bool getExtensionsForIbmCompatibleSystem(
113 const std::map<std::string, std::vector<std::string>>& extensionMap,
114 const std::vector<std::string>& ibmCompatibleSystem,
115 std::vector<std::string>& extensions)
116{
117 for (const auto& system : ibmCompatibleSystem)
118 {
119 auto extensionMapIterator = extensionMap.find(system);
120 if (extensionMapIterator != extensionMap.end())
121 {
122 extensions = extensionMapIterator->second;
123 return true;
124 }
125 }
126
127 return false;
128}
129
130/**
131 * @brief Write host firmware well-known name
132 *
133 * A wrapper around std::filesystem::create_symlink that avoids EEXIST by
134 * deleting any pre-existing file.
135 *
136 * @param[in] linkTarget The link target argument to
137 * std::filesystem::create_symlink
138 * @param[in] linkPath The link path argument to std::filesystem::create_symlink
139 * @param[in] errorCallback A callback made in the event of filesystem errors.
140 */
141void writeLink(const std::filesystem::path& linkTarget,
142 const std::filesystem::path& linkPath,
143 const ErrorCallbackType& errorCallback)
144{
145 std::error_code ec;
146
147 // remove files with the same name as the symlink to be created,
148 // otherwise symlink will fail with EEXIST.
149 if (!std::filesystem::remove(linkPath, ec))
150 {
151 if (ec)
152 {
153 makeCallback(errorCallback, linkPath, ec);
154 return;
155 }
156 }
157
158 std::filesystem::create_symlink(linkTarget, linkPath, ec);
159 if (ec)
160 {
161 makeCallback(errorCallback, linkPath, ec);
162 return;
163 }
164}
165
166/**
167 * @brief Find host firmware blob files that need well-known names
168 *
169 * The IBM host firmware runtime looks for data and/or additional code while
Manojkiran Eda96442c82024-06-17 10:24:05 +0530170 * bootstrapping in files with well-known names. findLinks uses the provided
Brad Bishop099543e2020-11-09 15:37:58 -0500171 * extensions argument to find host firmware blob files that require a
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600172 * well-known name. When a blob is found, issue the provided callback
Brad Bishop099543e2020-11-09 15:37:58 -0500173 * (typically a function that will write a symlink).
174 *
175 * @param[in] hostFirmwareDirectory The directory in which findLinks should
176 * look for host firmware blob files that need well-known names.
Manojkiran Eda96442c82024-06-17 10:24:05 +0530177 * @param[in] extensions The extensions of the firmware blob files denote a
Brad Bishop099543e2020-11-09 15:37:58 -0500178 * host firmware blob file requires a well-known name.
179 * @param[in] errorCallback A callback made in the event of filesystem errors.
180 * @param[in] linkCallback A callback made when host firmware blob files
181 * needing a well known name are found.
182 */
183void findLinks(const std::filesystem::path& hostFirmwareDirectory,
184 const std::vector<std::string>& extensions,
185 const ErrorCallbackType& errorCallback,
186 const LinkCallbackType& linkCallback)
187{
188 std::error_code ec;
Patrick Williamsf8e02422024-08-16 15:19:59 -0400189 std::filesystem::directory_iterator directoryIterator(
190 hostFirmwareDirectory, ec);
Brad Bishop099543e2020-11-09 15:37:58 -0500191 if (ec)
192 {
193 makeCallback(errorCallback, hostFirmwareDirectory, ec);
194 return;
195 }
196
Adriana Kobylak4e82bc82021-09-28 19:25:39 +0000197 // Create a symlink for pnor.toc
198 static const auto tocLid = "81e00994.lid";
199 auto tocLidPath = hostFirmwareDirectory / tocLid;
200 if (std::filesystem::exists(tocLidPath))
201 {
Adriana Kobylak4e82bc82021-09-28 19:25:39 +0000202 auto tocLinkPath = hostFirmwareDirectory / tocName;
203 makeCallback(linkCallback, tocLid, tocLinkPath, errorCallback);
204 }
205
Brad Bishop099543e2020-11-09 15:37:58 -0500206 for (; directoryIterator != std::filesystem::end(directoryIterator);
207 directoryIterator.increment(ec))
208 {
209 const auto& file = directoryIterator->path();
210 if (ec)
211 {
212 makeCallback(errorCallback, file, ec);
213 // quit here if the increment call failed otherwise the loop may
214 // never finish
215 break;
216 }
217
218 if (std::find(extensions.begin(), extensions.end(), file.extension()) ==
219 extensions.end())
220 {
221 // this file doesn't have an extension or doesn't match any of the
222 // provided extensions.
223 continue;
224 }
225
226 auto linkPath(file.parent_path().append(
227 static_cast<const std::string&>(file.stem())));
228
229 makeCallback(linkCallback, file.filename(), linkPath, errorCallback);
230 }
231}
232
233/**
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000234 * @brief Parse the elements json file and construct a string with the data to
235 * be used to update the bios attribute table.
236 *
237 * @param[in] elementsJsonFilePath - The path to the host firmware json file.
238 * @param[in] extensions - The extensions of the firmware blob files.
Adriana Kobylak53a27392021-06-14 17:42:40 +0000239 */
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000240std::string getBiosAttrStr(const std::filesystem::path& elementsJsonFilePath,
241 const std::vector<std::string>& extensions)
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000242{
243 std::string biosAttrStr{};
244
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000245 std::ifstream jsonFile(elementsJsonFilePath.c_str());
246 if (!jsonFile)
247 {
248 return {};
249 }
250
251 std::map<std::string, std::string> attr;
252 auto data = nlohmann::json::parse(jsonFile, nullptr, false);
253 if (data.is_discarded())
254 {
255 log<level::ERR>("Error parsing JSON file",
256 entry("FILE=%s", elementsJsonFilePath.c_str()));
257 return {};
258 }
259
260 // .get requires a non-const iterator
261 for (auto& iter : data["lids"])
262 {
263 std::string name{};
264 std::string lid{};
265
266 try
267 {
268 name = iter["element_name"].get<std::string>();
269 lid = iter["short_lid_name"].get<std::string>();
270 }
Patrick Williams97a709b2021-10-06 13:05:40 -0500271 catch (const std::exception& e)
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000272 {
273 // Possibly the element or lid name field was not found
274 log<level::ERR>("Error reading JSON field",
275 entry("FILE=%s", elementsJsonFilePath.c_str()),
276 entry("ERROR=%s", e.what()));
277 continue;
278 }
279
280 // The elements with the ipl extension have higher priority. Therefore
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000281 // Use operator[] to overwrite value if an entry for it already exists,
282 // and create a second entry with key name element_RT to specify it as
283 // a runtime element.
284 // Ex: if the JSON contains an entry A.P10 with lid name X, it'll create
285 // and try A=X. If the JSON also contained an entry A.P10.iplTime with
286 // lid name Y, the A entry would be overwritten to be A=Y and a second
287 // entry A_RT=X would be created.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000288 constexpr auto iplExtension = ".iplTime";
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000289 constexpr auto runtimeSuffix = "_RT";
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000290 std::filesystem::path path(name);
291 if (path.extension() == iplExtension)
292 {
293 // Some elements have an additional extension, ex: .P10.iplTime
294 // Strip off the ipl extension with stem(), then check if there is
295 // an additional extension with extension().
296 if (!path.stem().extension().empty())
297 {
298 // Check if the extension matches the extensions for this system
299 if (std::find(extensions.begin(), extensions.end(),
300 path.stem().extension()) == extensions.end())
301 {
302 continue;
303 }
304 }
305 // Get the element name without extensions by calling stem() twice
306 // since stem() returns the base name if no periods are found.
307 // Therefore both "element.P10" and "element.P10.iplTime" would
308 // become "element".
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000309 auto keyName = path.stem().stem();
310 auto attrIt = attr.find(keyName);
311 if (attrIt != attr.end())
312 {
313 // Copy the existing entry to a runtime entry
314 auto runtimeKeyName = keyName.string() + runtimeSuffix;
315 attr.insert({runtimeKeyName, attrIt->second});
316 }
Manojkiran Eda96442c82024-06-17 10:24:05 +0530317 // Overwrite the existing element with the ipl entry
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000318 attr[keyName] = lid;
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000319 continue;
320 }
321
322 // Process all other extensions. The extension should match the list of
323 // supported extensions for this system. Use .insert() to only add
324 // entries that do not exist, so to not overwrite the values that may
325 // had been added that had the ipl extension.
326 if (std::find(extensions.begin(), extensions.end(), path.extension()) !=
327 extensions.end())
328 {
Adriana Kobylak9cbc06b2021-11-09 22:02:44 +0000329 auto keyName = path.stem();
330 auto attrIt = attr.find(keyName);
331 if (attrIt != attr.end())
332 {
333 // The existing entry is an ipl entry, therefore create this
334 // entry as a runtime one.
335 auto runtimeKeyName = keyName.string() + runtimeSuffix;
336 attr.insert({runtimeKeyName, lid});
337 }
338 else
339 {
340 attr.insert({path.stem(), lid});
341 }
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000342 }
343 }
344 for (const auto& a : attr)
345 {
346 // Build the bios attribute string with format:
347 // "element1=lid1,element2=lid2,elementN=lidN,"
348 biosAttrStr += a.first + "=" + a.second + ",";
Adriana Kobylak5dc5d6c2021-07-20 20:21:16 +0000349
Adriana Kobylakf3b0c282024-10-15 14:14:59 -0500350 std::error_code ec;
351 auto lidName = a.second + ".lid";
Patrick Williamsf8e02422024-08-16 15:19:59 -0400352 auto elementFilePath =
353 std::filesystem::path("/media/hostfw/running") / a.first;
Adriana Kobylakf3b0c282024-10-15 14:14:59 -0500354
355 // Remove the symlink if the target does not match so that it gets
356 // recreated. Ignore pnor.toc, this symlink is manually created by the
357 // function findLinks().
358 if ((a.first != tocName) &&
359 std::filesystem::is_symlink(elementFilePath, ec))
360 {
361 auto target = std::filesystem::read_symlink(elementFilePath, ec);
362 if (target != lidName)
363 {
364 log<level::INFO>("Removing mismatched symlilnk",
365 entry("LINK=%s", elementFilePath.c_str()),
366 entry("TARGET=%s", target.c_str()),
367 entry("EXPECTED:%s", lidName.c_str()));
368 std::filesystem::remove(elementFilePath, ec);
369 }
370 }
371
372 // Create symlinks from the hostfw elements to their corresponding
373 // lid files if they don't exist
Adriana Kobylak5dc5d6c2021-07-20 20:21:16 +0000374 if (!std::filesystem::exists(elementFilePath))
375 {
Adriana Kobylak5dc5d6c2021-07-20 20:21:16 +0000376 std::filesystem::create_symlink(lidName, elementFilePath, ec);
377 if (ec)
378 {
379 log<level::ERR>("Error creating symlink",
380 entry("TARGET=%s", lidName.c_str()),
381 entry("LINK=%s", elementFilePath.c_str()));
382 }
383 }
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000384 }
385
George Liua38f6e62021-09-20 08:53:00 +0800386 // Delete the last comma of the bios attribute string
387 if (biosAttrStr.back() == ',')
388 {
389 return biosAttrStr.substr(0, biosAttrStr.length() - 1);
390 }
391
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000392 return biosAttrStr;
393}
394
395/**
396 * @brief Set the bios attribute table with details of the host firmware data
397 * for this system.
398 *
399 * @param[in] elementsJsonFilePath - The path to the host firmware json file.
Manojkiran Eda96442c82024-06-17 10:24:05 +0530400 * @param[in] extensions - The extensions of the firmware blob files.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000401 */
402void setBiosAttr(const std::filesystem::path& elementsJsonFilePath,
403 const std::vector<std::string>& extensions)
404{
405 auto biosAttrStr = getBiosAttrStr(elementsJsonFilePath, extensions);
406
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000407 constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager";
408 constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager";
409 constexpr auto dbusAttrName = "hb_lid_ids";
410 constexpr auto dbusAttrType =
411 "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
412
413 using PendingAttributesType = std::vector<std::pair<
414 std::string, std::tuple<std::string, std::variant<std::string>>>>;
415 PendingAttributesType pendingAttributes;
416 pendingAttributes.emplace_back(std::make_pair(
417 dbusAttrName, std::make_tuple(dbusAttrType, biosAttrStr)));
418
419 auto bus = sdbusplus::bus::new_default();
420 auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
421 MAPPER_INTERFACE, "GetObject");
422 method.append(biosConfigPath, std::vector<std::string>({biosConfigIntf}));
423 std::vector<std::pair<std::string, std::vector<std::string>>> response;
424 try
425 {
426 auto reply = bus.call(method);
427 reply.read(response);
428 if (response.empty())
429 {
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000430 log<level::INFO>("Error reading mapper response",
431 entry("PATH=%s", biosConfigPath),
432 entry("INTERFACE=%s", biosConfigIntf));
433 throw sdbusplus::xyz::openbmc_project::Common::Error::
434 InternalFailure();
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000435 }
436 auto method = bus.new_method_call((response.begin()->first).c_str(),
437 biosConfigPath,
438 SYSTEMD_PROPERTY_INTERFACE, "Set");
439 method.append(biosConfigIntf, "PendingAttributes",
440 std::variant<PendingAttributesType>(pendingAttributes));
441 bus.call(method);
442 }
Patrick Williams0dea1992022-07-22 19:26:52 -0500443 catch (const sdbusplus::exception_t& e)
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000444 {
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000445 log<level::INFO>("Error setting the bios attribute",
446 entry("ERROR=%s", e.what()),
447 entry("ATTRIBUTE=%s", dbusAttrName));
448 throw;
Adriana Kobylak56f538c2021-06-16 20:37:21 +0000449 }
450}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000451
452/**
Brad Bishop099543e2020-11-09 15:37:58 -0500453 * @brief Make callbacks on
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600454 * xyz.openbmc_project.Inventory.Decorator.Compatible instances.
Brad Bishop099543e2020-11-09 15:37:58 -0500455 *
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600456 * Look for an instance of xyz.openbmc_project.Inventory.Decorator.Compatible in
457 * the provided argument and if found, issue the provided callback.
Brad Bishop099543e2020-11-09 15:37:58 -0500458 *
459 * @param[in] interfacesAndProperties the interfaces in which to look for an
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600460 * instance of xyz.openbmc_project.Inventory.Decorator.Compatible
Brad Bishop099543e2020-11-09 15:37:58 -0500461 * @param[in] callback the user callback to make if
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600462 * xyz.openbmc_project.Inventory.Decorator.Compatible is found in
Brad Bishop099543e2020-11-09 15:37:58 -0500463 * interfacesAndProperties
464 * @return true if interfacesAndProperties contained an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600465 * xyz.openbmc_project.Inventory.Decorator.Compatible, false otherwise
Brad Bishop099543e2020-11-09 15:37:58 -0500466 */
Patrick Williamsf8e02422024-08-16 15:19:59 -0400467bool maybeCall(
468 const std::map<
469 std::string,
470 std::map<std::string, std::variant<std::vector<std::string>>>>&
471 interfacesAndProperties,
472 const MaybeCallCallbackType& callback)
Brad Bishop099543e2020-11-09 15:37:58 -0500473{
474 using namespace std::string_literals;
475
476 static const auto interfaceName =
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600477 "xyz.openbmc_project.Inventory.Decorator.Compatible"s;
Brad Bishop099543e2020-11-09 15:37:58 -0500478 auto interfaceIterator = interfacesAndProperties.find(interfaceName);
479 if (interfaceIterator == interfacesAndProperties.cend())
480 {
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600481 // Compatible interface not found, so instruct the caller to keep
482 // waiting or try again later.
Brad Bishop099543e2020-11-09 15:37:58 -0500483 return false;
484 }
485 auto propertyIterator = interfaceIterator->second.find("Names"s);
486 if (propertyIterator == interfaceIterator->second.cend())
487 {
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600488 // The interface exists but the property doesn't. This is a bug in the
489 // Compatible implementation. The caller should not try again.
Brad Bishop099543e2020-11-09 15:37:58 -0500490 std::cerr << "Names property not implemented on " << interfaceName
491 << "\n";
492 return true;
493 }
494
495 const auto& ibmCompatibleSystem =
496 std::get<std::vector<std::string>>(propertyIterator->second);
497 if (callback)
498 {
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000499 try
500 {
501 callback(ibmCompatibleSystem);
502 }
Patrick Williams0dea1992022-07-22 19:26:52 -0500503 catch (const sdbusplus::exception_t& e)
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000504 {
505 return false;
506 }
Brad Bishop099543e2020-11-09 15:37:58 -0500507 }
508
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600509 // Compatible found and callback issued.
Brad Bishop099543e2020-11-09 15:37:58 -0500510 return true;
511}
512
513/**
514 * @brief Make callbacks on
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600515 * xyz.openbmc_project.Inventory.Decorator.Compatible instances.
Brad Bishop099543e2020-11-09 15:37:58 -0500516 *
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600517 * Look for an instance ofxyz.openbmc_project.Inventory.Decorator.Compatible in
518 * the provided argument and if found, issue the provided callback.
Brad Bishop099543e2020-11-09 15:37:58 -0500519 *
520 * @param[in] message the DBus message in which to look for an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600521 * xyz.openbmc_project.Inventory.Decorator.Compatible
Brad Bishop099543e2020-11-09 15:37:58 -0500522 * @param[in] callback the user callback to make if
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600523 * xyz.openbmc_project.Inventory.Decorator.Compatible is found in message
Brad Bishop099543e2020-11-09 15:37:58 -0500524 * @return true if message contained an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600525 * xyz.openbmc_project.Inventory.Decorator.Compatible, false otherwise
Brad Bishop099543e2020-11-09 15:37:58 -0500526 */
Patrick Williams0dea1992022-07-22 19:26:52 -0500527bool maybeCallMessage(sdbusplus::message_t& message,
Brad Bishop099543e2020-11-09 15:37:58 -0500528 const MaybeCallCallbackType& callback)
529{
530 std::map<std::string,
531 std::map<std::string, std::variant<std::vector<std::string>>>>
532 interfacesAndProperties;
533 sdbusplus::message::object_path _;
534 message.read(_, interfacesAndProperties);
535 return maybeCall(interfacesAndProperties, callback);
536}
537
538/**
539 * @brief Determine system support for host firmware well-known names.
540 *
541 * Using the provided extensionMap and
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600542 * xyz.openbmc_project.Inventory.Decorator.Compatible, determine if well-known
Manojkiran Eda96442c82024-06-17 10:24:05 +0530543 * names for host firmware blob files are necessary and if so, create them.
Brad Bishop099543e2020-11-09 15:37:58 -0500544 *
545 * @param[in] extensionMap a map of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600546 * xyz.openbmc_project.Inventory.Decorator.Compatible to host firmware blob file
547 * extensions.
548 * @param[in] hostFirmwareDirectory The directory in which findLinks should look
549 * for host firmware blob files that need well-known names.
Brad Bishop099543e2020-11-09 15:37:58 -0500550 * @param[in] ibmCompatibleSystem The names property of an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600551 * xyz.openbmc_project.Inventory.Decorator.Compatible
Brad Bishop099543e2020-11-09 15:37:58 -0500552 * @param[in] errorCallback A callback made in the event of filesystem errors.
553 */
554void maybeMakeLinks(
555 const std::map<std::string, std::vector<std::string>>& extensionMap,
556 const std::filesystem::path& hostFirmwareDirectory,
557 const std::vector<std::string>& ibmCompatibleSystem,
558 const ErrorCallbackType& errorCallback)
559{
560 std::vector<std::string> extensions;
561 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
562 extensions))
563 {
564 findLinks(hostFirmwareDirectory, extensions, errorCallback, writeLink);
565 }
566}
567
568/**
Adriana Kobylak53a27392021-06-14 17:42:40 +0000569 * @brief Determine system support for updating the bios attribute table.
570 *
571 * Using the provided extensionMap and
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600572 * xyz.openbmc_project.Inventory.Decorator.Compatible, determine if the bios
Adriana Kobylak53a27392021-06-14 17:42:40 +0000573 * attribute table needs to be updated.
574 *
575 * @param[in] extensionMap a map of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600576 * xyz.openbmc_project.Inventory.Decorator.Compatible to host firmware blob file
577 * extensions.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000578 * @param[in] elementsJsonFilePath The file path to the json file
Adriana Kobylak53a27392021-06-14 17:42:40 +0000579 * @param[in] ibmCompatibleSystem The names property of an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600580 * xyz.openbmc_project.Inventory.Decorator.Compatible
Adriana Kobylak53a27392021-06-14 17:42:40 +0000581 */
582void maybeSetBiosAttr(
583 const std::map<std::string, std::vector<std::string>>& extensionMap,
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000584 const std::filesystem::path& elementsJsonFilePath,
Adriana Kobylak53a27392021-06-14 17:42:40 +0000585 const std::vector<std::string>& ibmCompatibleSystem)
586{
587 std::vector<std::string> extensions;
588 if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem,
589 extensions))
590 {
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000591 try
592 {
593 setBiosAttr(elementsJsonFilePath, extensions);
594 }
Patrick Williams0dea1992022-07-22 19:26:52 -0500595 catch (const sdbusplus::exception_t& e)
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000596 {
597 throw;
598 }
Adriana Kobylak53a27392021-06-14 17:42:40 +0000599 }
600}
601
602/**
Brad Bishop099543e2020-11-09 15:37:58 -0500603 * @brief process host firmware
604 *
605 * Allocate a callback context and register for DBus.ObjectManager Interfaces
606 * added signals from entity manager.
607 *
608 * Check the current entity manager object tree for a
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600609 * xyz.openbmc_project.Inventory.Decorator.Compatible instance (entity manager
610 * will be dbus activated if it is not running). If one is found, determine if
611 * symlinks need to be created and create them. Instruct the program event loop
612 * to exit.
Brad Bishop099543e2020-11-09 15:37:58 -0500613 *
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600614 * If no instance of xyz.openbmc_project.Inventory.Decorator.Compatible is found
615 * return the callback context to main, where the program will sleep until the
616 * callback is invoked one or more times and instructs the program event loop to
617 * exit when xyz.openbmc_project.Inventory.Decorator.Compatible is added.
Brad Bishop099543e2020-11-09 15:37:58 -0500618 *
619 * @param[in] bus a DBus client connection
620 * @param[in] extensionMap a map of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600621 * xyz.openbmc_project.Inventory.Decorator.Compatible to host firmware blob file
622 * extensions.
Brad Bishop099543e2020-11-09 15:37:58 -0500623 * @param[in] hostFirmwareDirectory The directory in which processHostFirmware
624 * should look for blob files.
625 * @param[in] errorCallback A callback made in the event of filesystem errors.
626 * @param[in] loop a program event loop
627 * @return nullptr if an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600628 * xyz.openbmc_project.Inventory.Decorator.Compatible is found, otherwise a
Brad Bishop099543e2020-11-09 15:37:58 -0500629 * pointer to an sdbusplus match object.
630 */
631std::shared_ptr<void> processHostFirmware(
Patrick Williams0dea1992022-07-22 19:26:52 -0500632 sdbusplus::bus_t& bus,
Brad Bishop099543e2020-11-09 15:37:58 -0500633 std::map<std::string, std::vector<std::string>> extensionMap,
634 std::filesystem::path hostFirmwareDirectory,
635 ErrorCallbackType errorCallback, sdeventplus::Event& loop)
636{
637 // ownership of extensionMap, hostFirmwareDirectory and errorCallback can't
Manojkiran Eda96442c82024-06-17 10:24:05 +0530638 // be transferred to the match callback because they are needed in the non
Brad Bishop099543e2020-11-09 15:37:58 -0500639 // async part of this function below, so they need to be moved to the heap.
640 auto pExtensionMap =
641 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
642 auto pHostFirmwareDirectory =
643 std::make_shared<decltype(hostFirmwareDirectory)>(
644 std::move(hostFirmwareDirectory));
645 auto pErrorCallback =
646 std::make_shared<decltype(errorCallback)>(std::move(errorCallback));
647
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600648 // register for a callback in case the Compatible interface has not yet been
649 // published by entity manager.
Patrick Williams0dea1992022-07-22 19:26:52 -0500650 auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match_t>(
Brad Bishop099543e2020-11-09 15:37:58 -0500651 bus,
652 sdbusplus::bus::match::rules::interfacesAdded() +
653 sdbusplus::bus::match::rules::sender(
654 "xyz.openbmc_project.EntityManager"),
655 [pExtensionMap, pHostFirmwareDirectory, pErrorCallback,
656 &loop](auto& message) {
Patrick Williamsf8e02422024-08-16 15:19:59 -0400657 // bind the extension map, host firmware directory, and error
658 // callback to the maybeMakeLinks function.
659 auto maybeMakeLinksWithArgsBound =
660 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
661 std::cref(*pHostFirmwareDirectory),
662 std::placeholders::_1, std::cref(*pErrorCallback));
Brad Bishop099543e2020-11-09 15:37:58 -0500663
Patrick Williamsf8e02422024-08-16 15:19:59 -0400664 // if the InterfacesAdded message contains an an instance of
665 // xyz.openbmc_project.Inventory.Decorator.Compatible, check to see
666 // if links are necessary on this system and if so, create them.
667 if (maybeCallMessage(message, maybeMakeLinksWithArgsBound))
668 {
669 // The Compatible interface was found and the links were created
670 // if applicable. Instruct the event loop / subcommand to exit.
671 loop.exit(0);
672 }
673 });
Brad Bishop099543e2020-11-09 15:37:58 -0500674
675 // now that we'll get a callback in the event of an InterfacesAdded signal
676 // (potentially containing
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600677 // xyz.openbmc_project.Inventory.Decorator.Compatible), activate entity
Brad Bishop099543e2020-11-09 15:37:58 -0500678 // manager if it isn't running and enumerate its objects
679 auto getManagedObjects = bus.new_method_call(
Nan Zhou75352b42022-09-20 20:16:19 +0000680 "xyz.openbmc_project.EntityManager", "/xyz/openbmc_project/inventory",
Brad Bishop099543e2020-11-09 15:37:58 -0500681 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Brad Bishop099543e2020-11-09 15:37:58 -0500682 std::map<std::string,
683 std::map<std::string, std::variant<std::vector<std::string>>>>
684 interfacesAndProperties;
685 std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)>
686 objects;
Adriana Kobylakc79fa912021-06-22 15:37:50 +0000687 try
688 {
689 auto reply = bus.call(getManagedObjects);
690 reply.read(objects);
691 }
Patrick Williams0dea1992022-07-22 19:26:52 -0500692 catch (const sdbusplus::exception_t& e)
Adriana Kobylakc79fa912021-06-22 15:37:50 +0000693 {
694 // Error querying the EntityManager interface. Return the match to have
695 // the callback run if/when the interface appears in D-Bus.
696 return interfacesAddedMatch;
697 }
Brad Bishop099543e2020-11-09 15:37:58 -0500698
699 // bind the extension map, host firmware directory, and error callback to
700 // the maybeMakeLinks function.
701 auto maybeMakeLinksWithArgsBound =
702 std::bind(maybeMakeLinks, std::cref(*pExtensionMap),
703 std::cref(*pHostFirmwareDirectory), std::placeholders::_1,
704 std::cref(*pErrorCallback));
705
706 for (const auto& pair : objects)
707 {
708 std::tie(std::ignore, interfacesAndProperties) = pair;
709 // if interfacesAndProperties contains an an instance of
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600710 // xyz.openbmc_project.Inventory.Decorator.Compatible, check to see if
711 // links are necessary on this system and if so, create them
Brad Bishop099543e2020-11-09 15:37:58 -0500712 if (maybeCall(interfacesAndProperties, maybeMakeLinksWithArgsBound))
713 {
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600714 // The Compatible interface is already on the bus and the links were
715 // created if applicable. Instruct the event loop to exit.
Brad Bishop099543e2020-11-09 15:37:58 -0500716 loop.exit(0);
717 // The match object isn't needed anymore, so destroy it on return.
718 return nullptr;
719 }
720 }
721
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600722 // The Compatible interface has not yet been published. Move ownership of
723 // the match callback to the caller.
Brad Bishop099543e2020-11-09 15:37:58 -0500724 return interfacesAddedMatch;
725}
Adriana Kobylak53a27392021-06-14 17:42:40 +0000726
727/**
728 * @brief Update the Bios Attribute Table
729 *
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600730 * If an instance of xyz.openbmc_project.Inventory.Decorator.Compatible is
Adriana Kobylak53a27392021-06-14 17:42:40 +0000731 * found, update the Bios Attribute Table with the appropriate host firmware
732 * data.
733 *
734 * @param[in] bus - D-Bus client connection.
Adriana Kobylak749bdcc2024-01-17 12:56:00 -0600735 * @param[in] extensionMap - Map of Compatible names and host firmware file
736 extensions.
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000737 * @param[in] elementsJsonFilePath - The Path to the json file
Adriana Kobylak53a27392021-06-14 17:42:40 +0000738 * @param[in] loop - Program event loop.
739 * @return nullptr
740 */
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000741std::vector<std::shared_ptr<void>> updateBiosAttrTable(
Patrick Williams0dea1992022-07-22 19:26:52 -0500742 sdbusplus::bus_t& bus,
Adriana Kobylak53a27392021-06-14 17:42:40 +0000743 std::map<std::string, std::vector<std::string>> extensionMap,
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000744 std::filesystem::path elementsJsonFilePath, sdeventplus::Event& loop)
Adriana Kobylak53a27392021-06-14 17:42:40 +0000745{
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000746 constexpr auto pldmPath = "/xyz/openbmc_project/pldm";
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000747 constexpr auto entityManagerServiceName =
748 "xyz.openbmc_project.EntityManager";
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000749
Adriana Kobylak53a27392021-06-14 17:42:40 +0000750 auto pExtensionMap =
751 std::make_shared<decltype(extensionMap)>(std::move(extensionMap));
Adriana Kobylakae0998f2021-06-16 19:52:24 +0000752 auto pElementsJsonFilePath =
753 std::make_shared<decltype(elementsJsonFilePath)>(
754 std::move(elementsJsonFilePath));
Adriana Kobylak53a27392021-06-14 17:42:40 +0000755
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000756 auto maybeSetAttrWithArgsBound =
757 std::bind(maybeSetBiosAttr, std::cref(*pExtensionMap),
758 std::cref(*pElementsJsonFilePath), std::placeholders::_1);
759
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000760 std::vector<std::shared_ptr<void>> matches;
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000761
762 // Entity Manager is needed to get the list of supported extensions. Add a
763 // match to monitor interfaces added in case it's not running yet.
Patrick Williams0dea1992022-07-22 19:26:52 -0500764 matches.emplace_back(std::make_shared<sdbusplus::bus::match_t>(
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000765 bus,
766 sdbusplus::bus::match::rules::interfacesAdded() +
767 sdbusplus::bus::match::rules::sender(
768 "xyz.openbmc_project.EntityManager"),
769 [pldmPath, pExtensionMap, pElementsJsonFilePath,
770 maybeSetAttrWithArgsBound, &loop](auto& message) {
Patrick Williamsf8e02422024-08-16 15:19:59 -0400771 if (maybeCallMessage(message, maybeSetAttrWithArgsBound))
772 {
773 loop.exit(0);
774 }
775 }));
Adriana Kobylak2b78eb02022-04-06 19:38:47 +0000776
777 // The BIOS attribute table can only be updated if PLDM is running because
778 // PLDM is the one that exposes this property. Add a match to monitor when
779 // the PLDM service starts.
Patrick Williams0dea1992022-07-22 19:26:52 -0500780 matches.emplace_back(std::make_shared<sdbusplus::bus::match_t>(
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000781 bus,
782 sdbusplus::bus::match::rules::nameOwnerChanged() +
783 sdbusplus::bus::match::rules::arg0namespace(
784 "xyz.openbmc_project.PLDM"),
785 [pExtensionMap, pElementsJsonFilePath, maybeSetAttrWithArgsBound,
786 &loop](auto& message) {
Patrick Williamsf8e02422024-08-16 15:19:59 -0400787 std::string name;
788 std::string oldOwner;
789 std::string newOwner;
790 message.read(name, oldOwner, newOwner);
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000791
Patrick Williamsf8e02422024-08-16 15:19:59 -0400792 if (newOwner.empty())
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000793 {
Patrick Williamsf8e02422024-08-16 15:19:59 -0400794 return;
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000795 }
Patrick Williamsf8e02422024-08-16 15:19:59 -0400796
797 auto bus = sdbusplus::bus::new_default();
798 InterfacesPropertiesMap interfacesAndProperties;
799 auto objects = getManagedObjects(bus, entityManagerServiceName,
800 "/xyz/openbmc_project/inventory");
801 for (const auto& pair : objects)
802 {
803 std::tie(std::ignore, interfacesAndProperties) = pair;
804 if (maybeCall(interfacesAndProperties,
805 maybeSetAttrWithArgsBound))
806 {
807 loop.exit(0);
808 }
809 }
810 }));
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000811
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000812 InterfacesPropertiesMap interfacesAndProperties;
Nan Zhou75352b42022-09-20 20:16:19 +0000813 auto objects = getManagedObjects(bus, entityManagerServiceName,
814 "/xyz/openbmc_project/inventory");
Adriana Kobylak53a27392021-06-14 17:42:40 +0000815 for (const auto& pair : objects)
816 {
817 std::tie(std::ignore, interfacesAndProperties) = pair;
818 if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound))
819 {
Adriana Kobylakd0379ea2021-06-21 16:15:59 +0000820 loop.exit(0);
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000821 return {};
Adriana Kobylak53a27392021-06-14 17:42:40 +0000822 }
823 }
824
Adriana Kobylakebf67bf2021-06-21 18:38:17 +0000825 return matches;
Adriana Kobylak53a27392021-06-14 17:42:40 +0000826}
827
Brad Bishop099543e2020-11-09 15:37:58 -0500828} // namespace process_hostfirmware
829} // namespace functions