1099543e4SBrad Bishop // SPDX-License-Identifier: Apache-2.0 2099543e4SBrad Bishop 3099543e4SBrad Bishop /**@file functions.cpp*/ 4099543e4SBrad Bishop 556f538caSAdriana Kobylak #include "config.h" 656f538caSAdriana Kobylak 7099543e4SBrad Bishop #include "functions.hpp" 8099543e4SBrad Bishop 9ae0998f1SAdriana Kobylak #include <nlohmann/json.hpp> 1056f538caSAdriana Kobylak #include <phosphor-logging/log.hpp> 11099543e4SBrad Bishop #include <sdbusplus/bus.hpp> 12099543e4SBrad Bishop #include <sdbusplus/bus/match.hpp> 13c79fa915SAdriana Kobylak #include <sdbusplus/exception.hpp> 14099543e4SBrad Bishop #include <sdbusplus/message.hpp> 15099543e4SBrad Bishop #include <sdeventplus/event.hpp> 16099543e4SBrad Bishop 17099543e4SBrad Bishop #include <filesystem> 18ae0998f1SAdriana Kobylak #include <fstream> 19099543e4SBrad Bishop #include <functional> 20099543e4SBrad Bishop #include <iostream> 21099543e4SBrad Bishop #include <map> 22099543e4SBrad Bishop #include <memory> 23099543e4SBrad Bishop #include <string> 24099543e4SBrad Bishop #include <variant> 25099543e4SBrad Bishop #include <vector> 26099543e4SBrad Bishop 27099543e4SBrad Bishop namespace functions 28099543e4SBrad Bishop { 29099543e4SBrad Bishop namespace process_hostfirmware 30099543e4SBrad Bishop { 31099543e4SBrad Bishop 3256f538caSAdriana Kobylak using namespace phosphor::logging; 3356f538caSAdriana Kobylak 34099543e4SBrad Bishop /** 35*fd4a6088SAdriana Kobylak * @brief GetObject function to find the service given an object path. 36*fd4a6088SAdriana Kobylak * It is used to determine if a service is running, so there is no need 37*fd4a6088SAdriana Kobylak * to specify interfaces as a parameter to constrain the search. 38*fd4a6088SAdriana Kobylak */ 39*fd4a6088SAdriana Kobylak std::string getObject(sdbusplus::bus::bus& bus, const std::string& path) 40*fd4a6088SAdriana Kobylak { 41*fd4a6088SAdriana Kobylak auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 42*fd4a6088SAdriana Kobylak MAPPER_BUSNAME, "GetObject"); 43*fd4a6088SAdriana Kobylak method.append(path); 44*fd4a6088SAdriana Kobylak std::vector<std::string> interfaces; 45*fd4a6088SAdriana Kobylak method.append(interfaces); 46*fd4a6088SAdriana Kobylak 47*fd4a6088SAdriana Kobylak std::vector<std::pair<std::string, std::vector<std::string>>> response; 48*fd4a6088SAdriana Kobylak 49*fd4a6088SAdriana Kobylak try 50*fd4a6088SAdriana Kobylak { 51*fd4a6088SAdriana Kobylak auto reply = bus.call(method); 52*fd4a6088SAdriana Kobylak reply.read(response); 53*fd4a6088SAdriana Kobylak if (response.empty()) 54*fd4a6088SAdriana Kobylak { 55*fd4a6088SAdriana Kobylak return std::string{}; 56*fd4a6088SAdriana Kobylak } 57*fd4a6088SAdriana Kobylak } 58*fd4a6088SAdriana Kobylak catch (const sdbusplus::exception::SdBusError& e) 59*fd4a6088SAdriana Kobylak { 60*fd4a6088SAdriana Kobylak return std::string{}; 61*fd4a6088SAdriana Kobylak } 62*fd4a6088SAdriana Kobylak return response[0].first; 63*fd4a6088SAdriana Kobylak } 64*fd4a6088SAdriana Kobylak 65*fd4a6088SAdriana Kobylak /** 66099543e4SBrad Bishop * @brief Issue callbacks safely 67099543e4SBrad Bishop * 68099543e4SBrad Bishop * std::function can be empty, so this wrapper method checks for that prior to 69099543e4SBrad Bishop * calling it to avoid std::bad_function_call 70099543e4SBrad Bishop * 71099543e4SBrad Bishop * @tparam Sig the types of the std::function arguments 72099543e4SBrad Bishop * @tparam Args the deduced argument types 73099543e4SBrad Bishop * @param[in] callback the callback being wrapped 74099543e4SBrad Bishop * @param[in] args the callback arguments 75099543e4SBrad Bishop */ 76099543e4SBrad Bishop template <typename... Sig, typename... Args> 77099543e4SBrad Bishop void makeCallback(const std::function<void(Sig...)>& callback, Args&&... args) 78099543e4SBrad Bishop { 79099543e4SBrad Bishop if (callback) 80099543e4SBrad Bishop { 81099543e4SBrad Bishop callback(std::forward<Args>(args)...); 82099543e4SBrad Bishop } 83099543e4SBrad Bishop } 84099543e4SBrad Bishop 85099543e4SBrad Bishop /** 86099543e4SBrad Bishop * @brief Get file extensions for IBMCompatibleSystem 87099543e4SBrad Bishop * 88099543e4SBrad Bishop * IBM host firmware can be deployed as blobs (files) in a filesystem. Host 89099543e4SBrad Bishop * firmware blobs for different values of 90099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem are packaged with 91099543e4SBrad Bishop * different filename extensions. getExtensionsForIbmCompatibleSystem 92099543e4SBrad Bishop * maintains the mapping from a given value of 93099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem to an array of 94099543e4SBrad Bishop * filename extensions. 95099543e4SBrad Bishop * 96099543e4SBrad Bishop * If a mapping is found getExtensionsForIbmCompatibleSystem returns true and 97099543e4SBrad Bishop * the extensions parameter is reset with the map entry. If no mapping is 98099543e4SBrad Bishop * found getExtensionsForIbmCompatibleSystem returns false and extensions is 99099543e4SBrad Bishop * unmodified. 100099543e4SBrad Bishop * 101099543e4SBrad Bishop * @param[in] extensionMap a map of 102099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob 103099543e4SBrad Bishop * file extensions. 104099543e4SBrad Bishop * @param[in] ibmCompatibleSystem The names property of an instance of 105099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem 106099543e4SBrad Bishop * @param[out] extentions the host firmware blob file extensions 107099543e4SBrad Bishop * @return true if an entry was found, otherwise false 108099543e4SBrad Bishop */ 109099543e4SBrad Bishop bool getExtensionsForIbmCompatibleSystem( 110099543e4SBrad Bishop const std::map<std::string, std::vector<std::string>>& extensionMap, 111099543e4SBrad Bishop const std::vector<std::string>& ibmCompatibleSystem, 112099543e4SBrad Bishop std::vector<std::string>& extensions) 113099543e4SBrad Bishop { 114099543e4SBrad Bishop for (const auto& system : ibmCompatibleSystem) 115099543e4SBrad Bishop { 116099543e4SBrad Bishop auto extensionMapIterator = extensionMap.find(system); 117099543e4SBrad Bishop if (extensionMapIterator != extensionMap.end()) 118099543e4SBrad Bishop { 119099543e4SBrad Bishop extensions = extensionMapIterator->second; 120099543e4SBrad Bishop return true; 121099543e4SBrad Bishop } 122099543e4SBrad Bishop } 123099543e4SBrad Bishop 124099543e4SBrad Bishop return false; 125099543e4SBrad Bishop } 126099543e4SBrad Bishop 127099543e4SBrad Bishop /** 128099543e4SBrad Bishop * @brief Write host firmware well-known name 129099543e4SBrad Bishop * 130099543e4SBrad Bishop * A wrapper around std::filesystem::create_symlink that avoids EEXIST by 131099543e4SBrad Bishop * deleting any pre-existing file. 132099543e4SBrad Bishop * 133099543e4SBrad Bishop * @param[in] linkTarget The link target argument to 134099543e4SBrad Bishop * std::filesystem::create_symlink 135099543e4SBrad Bishop * @param[in] linkPath The link path argument to std::filesystem::create_symlink 136099543e4SBrad Bishop * @param[in] errorCallback A callback made in the event of filesystem errors. 137099543e4SBrad Bishop */ 138099543e4SBrad Bishop void writeLink(const std::filesystem::path& linkTarget, 139099543e4SBrad Bishop const std::filesystem::path& linkPath, 140099543e4SBrad Bishop const ErrorCallbackType& errorCallback) 141099543e4SBrad Bishop { 142099543e4SBrad Bishop std::error_code ec; 143099543e4SBrad Bishop 144099543e4SBrad Bishop // remove files with the same name as the symlink to be created, 145099543e4SBrad Bishop // otherwise symlink will fail with EEXIST. 146099543e4SBrad Bishop if (!std::filesystem::remove(linkPath, ec)) 147099543e4SBrad Bishop { 148099543e4SBrad Bishop if (ec) 149099543e4SBrad Bishop { 150099543e4SBrad Bishop makeCallback(errorCallback, linkPath, ec); 151099543e4SBrad Bishop return; 152099543e4SBrad Bishop } 153099543e4SBrad Bishop } 154099543e4SBrad Bishop 155099543e4SBrad Bishop std::filesystem::create_symlink(linkTarget, linkPath, ec); 156099543e4SBrad Bishop if (ec) 157099543e4SBrad Bishop { 158099543e4SBrad Bishop makeCallback(errorCallback, linkPath, ec); 159099543e4SBrad Bishop return; 160099543e4SBrad Bishop } 161099543e4SBrad Bishop } 162099543e4SBrad Bishop 163099543e4SBrad Bishop /** 164099543e4SBrad Bishop * @brief Find host firmware blob files that need well-known names 165099543e4SBrad Bishop * 166099543e4SBrad Bishop * The IBM host firmware runtime looks for data and/or additional code while 167099543e4SBrad Bishop * bootstraping in files with well-known names. findLinks uses the provided 168099543e4SBrad Bishop * extensions argument to find host firmware blob files that require a 169099543e4SBrad Bishop * well-known name. When a blob is found, issue the provided callback 170099543e4SBrad Bishop * (typically a function that will write a symlink). 171099543e4SBrad Bishop * 172099543e4SBrad Bishop * @param[in] hostFirmwareDirectory The directory in which findLinks should 173099543e4SBrad Bishop * look for host firmware blob files that need well-known names. 174099543e4SBrad Bishop * @param[in] extentions The extensions of the firmware blob files denote a 175099543e4SBrad Bishop * host firmware blob file requires a well-known name. 176099543e4SBrad Bishop * @param[in] errorCallback A callback made in the event of filesystem errors. 177099543e4SBrad Bishop * @param[in] linkCallback A callback made when host firmware blob files 178099543e4SBrad Bishop * needing a well known name are found. 179099543e4SBrad Bishop */ 180099543e4SBrad Bishop void findLinks(const std::filesystem::path& hostFirmwareDirectory, 181099543e4SBrad Bishop const std::vector<std::string>& extensions, 182099543e4SBrad Bishop const ErrorCallbackType& errorCallback, 183099543e4SBrad Bishop const LinkCallbackType& linkCallback) 184099543e4SBrad Bishop { 185099543e4SBrad Bishop std::error_code ec; 186099543e4SBrad Bishop std::filesystem::directory_iterator directoryIterator(hostFirmwareDirectory, 187099543e4SBrad Bishop ec); 188099543e4SBrad Bishop if (ec) 189099543e4SBrad Bishop { 190099543e4SBrad Bishop makeCallback(errorCallback, hostFirmwareDirectory, ec); 191099543e4SBrad Bishop return; 192099543e4SBrad Bishop } 193099543e4SBrad Bishop 194fdc91fa0SAdriana Kobylak // Create a symlink from HBB to the corresponding LID file if it exists 195fdc91fa0SAdriana Kobylak static const auto hbbLid = "81e0065a.lid"; 196fdc91fa0SAdriana Kobylak auto hbbLidPath = hostFirmwareDirectory / hbbLid; 197fdc91fa0SAdriana Kobylak if (std::filesystem::exists(hbbLidPath)) 198fdc91fa0SAdriana Kobylak { 199fdc91fa0SAdriana Kobylak static const auto hbbName = "HBB"; 200fdc91fa0SAdriana Kobylak auto hbbLinkPath = hostFirmwareDirectory / hbbName; 201fdc91fa0SAdriana Kobylak makeCallback(linkCallback, hbbLid, hbbLinkPath, errorCallback); 202fdc91fa0SAdriana Kobylak } 203fdc91fa0SAdriana Kobylak 204099543e4SBrad Bishop for (; directoryIterator != std::filesystem::end(directoryIterator); 205099543e4SBrad Bishop directoryIterator.increment(ec)) 206099543e4SBrad Bishop { 207099543e4SBrad Bishop const auto& file = directoryIterator->path(); 208099543e4SBrad Bishop if (ec) 209099543e4SBrad Bishop { 210099543e4SBrad Bishop makeCallback(errorCallback, file, ec); 211099543e4SBrad Bishop // quit here if the increment call failed otherwise the loop may 212099543e4SBrad Bishop // never finish 213099543e4SBrad Bishop break; 214099543e4SBrad Bishop } 215099543e4SBrad Bishop 216099543e4SBrad Bishop if (std::find(extensions.begin(), extensions.end(), file.extension()) == 217099543e4SBrad Bishop extensions.end()) 218099543e4SBrad Bishop { 219099543e4SBrad Bishop // this file doesn't have an extension or doesn't match any of the 220099543e4SBrad Bishop // provided extensions. 221099543e4SBrad Bishop continue; 222099543e4SBrad Bishop } 223099543e4SBrad Bishop 224099543e4SBrad Bishop auto linkPath(file.parent_path().append( 225099543e4SBrad Bishop static_cast<const std::string&>(file.stem()))); 226099543e4SBrad Bishop 227099543e4SBrad Bishop makeCallback(linkCallback, file.filename(), linkPath, errorCallback); 228099543e4SBrad Bishop } 229099543e4SBrad Bishop } 230099543e4SBrad Bishop 231099543e4SBrad Bishop /** 232ae0998f1SAdriana Kobylak * @brief Parse the elements json file and construct a string with the data to 233ae0998f1SAdriana Kobylak * be used to update the bios attribute table. 234ae0998f1SAdriana Kobylak * 235ae0998f1SAdriana Kobylak * @param[in] elementsJsonFilePath - The path to the host firmware json file. 236ae0998f1SAdriana Kobylak * @param[in] extensions - The extensions of the firmware blob files. 23753a27395SAdriana Kobylak */ 238ae0998f1SAdriana Kobylak std::string getBiosAttrStr(const std::filesystem::path& elementsJsonFilePath, 239ae0998f1SAdriana Kobylak const std::vector<std::string>& extensions) 24056f538caSAdriana Kobylak { 24156f538caSAdriana Kobylak std::string biosAttrStr{}; 24256f538caSAdriana Kobylak 243ae0998f1SAdriana Kobylak std::ifstream jsonFile(elementsJsonFilePath.c_str()); 244ae0998f1SAdriana Kobylak if (!jsonFile) 245ae0998f1SAdriana Kobylak { 246ae0998f1SAdriana Kobylak return {}; 247ae0998f1SAdriana Kobylak } 248ae0998f1SAdriana Kobylak 249ae0998f1SAdriana Kobylak std::map<std::string, std::string> attr; 250ae0998f1SAdriana Kobylak auto data = nlohmann::json::parse(jsonFile, nullptr, false); 251ae0998f1SAdriana Kobylak if (data.is_discarded()) 252ae0998f1SAdriana Kobylak { 253ae0998f1SAdriana Kobylak log<level::ERR>("Error parsing JSON file", 254ae0998f1SAdriana Kobylak entry("FILE=%s", elementsJsonFilePath.c_str())); 255ae0998f1SAdriana Kobylak return {}; 256ae0998f1SAdriana Kobylak } 257ae0998f1SAdriana Kobylak 258ae0998f1SAdriana Kobylak // .get requires a non-const iterator 259ae0998f1SAdriana Kobylak for (auto& iter : data["lids"]) 260ae0998f1SAdriana Kobylak { 261ae0998f1SAdriana Kobylak std::string name{}; 262ae0998f1SAdriana Kobylak std::string lid{}; 263ae0998f1SAdriana Kobylak 264ae0998f1SAdriana Kobylak try 265ae0998f1SAdriana Kobylak { 266ae0998f1SAdriana Kobylak name = iter["element_name"].get<std::string>(); 267ae0998f1SAdriana Kobylak lid = iter["short_lid_name"].get<std::string>(); 268ae0998f1SAdriana Kobylak } 269ae0998f1SAdriana Kobylak catch (std::exception& e) 270ae0998f1SAdriana Kobylak { 271ae0998f1SAdriana Kobylak // Possibly the element or lid name field was not found 272ae0998f1SAdriana Kobylak log<level::ERR>("Error reading JSON field", 273ae0998f1SAdriana Kobylak entry("FILE=%s", elementsJsonFilePath.c_str()), 274ae0998f1SAdriana Kobylak entry("ERROR=%s", e.what())); 275ae0998f1SAdriana Kobylak continue; 276ae0998f1SAdriana Kobylak } 277ae0998f1SAdriana Kobylak 278ae0998f1SAdriana Kobylak // The elements with the ipl extension have higher priority. Therefore 279ae0998f1SAdriana Kobylak // Use operator[] to overwrite value if an entry for it already exists. 280ae0998f1SAdriana Kobylak // Ex: if the JSON contains an entry A.P10 followed by A.P10.iplTime, 281ae0998f1SAdriana Kobylak // the lid value for the latter one will be overwrite the value of the 282ae0998f1SAdriana Kobylak // first one. 283ae0998f1SAdriana Kobylak constexpr auto iplExtension = ".iplTime"; 284ae0998f1SAdriana Kobylak std::filesystem::path path(name); 285ae0998f1SAdriana Kobylak if (path.extension() == iplExtension) 286ae0998f1SAdriana Kobylak { 287ae0998f1SAdriana Kobylak // Some elements have an additional extension, ex: .P10.iplTime 288ae0998f1SAdriana Kobylak // Strip off the ipl extension with stem(), then check if there is 289ae0998f1SAdriana Kobylak // an additional extension with extension(). 290ae0998f1SAdriana Kobylak if (!path.stem().extension().empty()) 291ae0998f1SAdriana Kobylak { 292ae0998f1SAdriana Kobylak // Check if the extension matches the extensions for this system 293ae0998f1SAdriana Kobylak if (std::find(extensions.begin(), extensions.end(), 294ae0998f1SAdriana Kobylak path.stem().extension()) == extensions.end()) 295ae0998f1SAdriana Kobylak { 296ae0998f1SAdriana Kobylak continue; 297ae0998f1SAdriana Kobylak } 298ae0998f1SAdriana Kobylak } 299ae0998f1SAdriana Kobylak // Get the element name without extensions by calling stem() twice 300ae0998f1SAdriana Kobylak // since stem() returns the base name if no periods are found. 301ae0998f1SAdriana Kobylak // Therefore both "element.P10" and "element.P10.iplTime" would 302ae0998f1SAdriana Kobylak // become "element". 303ae0998f1SAdriana Kobylak attr[path.stem().stem()] = lid; 304ae0998f1SAdriana Kobylak continue; 305ae0998f1SAdriana Kobylak } 306ae0998f1SAdriana Kobylak 307ae0998f1SAdriana Kobylak // Process all other extensions. The extension should match the list of 308ae0998f1SAdriana Kobylak // supported extensions for this system. Use .insert() to only add 309ae0998f1SAdriana Kobylak // entries that do not exist, so to not overwrite the values that may 310ae0998f1SAdriana Kobylak // had been added that had the ipl extension. 311ae0998f1SAdriana Kobylak if (std::find(extensions.begin(), extensions.end(), path.extension()) != 312ae0998f1SAdriana Kobylak extensions.end()) 313ae0998f1SAdriana Kobylak { 314ae0998f1SAdriana Kobylak attr.insert({path.stem(), lid}); 315ae0998f1SAdriana Kobylak } 316ae0998f1SAdriana Kobylak } 317ae0998f1SAdriana Kobylak for (const auto& a : attr) 318ae0998f1SAdriana Kobylak { 319ae0998f1SAdriana Kobylak // Build the bios attribute string with format: 320ae0998f1SAdriana Kobylak // "element1=lid1,element2=lid2,elementN=lidN," 321ae0998f1SAdriana Kobylak biosAttrStr += a.first + "=" + a.second + ","; 322ae0998f1SAdriana Kobylak } 323ae0998f1SAdriana Kobylak 324ae0998f1SAdriana Kobylak return biosAttrStr; 325ae0998f1SAdriana Kobylak } 326ae0998f1SAdriana Kobylak 327ae0998f1SAdriana Kobylak /** 328ae0998f1SAdriana Kobylak * @brief Set the bios attribute table with details of the host firmware data 329ae0998f1SAdriana Kobylak * for this system. 330ae0998f1SAdriana Kobylak * 331ae0998f1SAdriana Kobylak * @param[in] elementsJsonFilePath - The path to the host firmware json file. 332ae0998f1SAdriana Kobylak * @param[in] extentions - The extensions of the firmware blob files. 333ae0998f1SAdriana Kobylak */ 334ae0998f1SAdriana Kobylak void setBiosAttr(const std::filesystem::path& elementsJsonFilePath, 335ae0998f1SAdriana Kobylak const std::vector<std::string>& extensions) 336ae0998f1SAdriana Kobylak { 337ae0998f1SAdriana Kobylak auto biosAttrStr = getBiosAttrStr(elementsJsonFilePath, extensions); 338ae0998f1SAdriana Kobylak 33956f538caSAdriana Kobylak constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager"; 34056f538caSAdriana Kobylak constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager"; 34156f538caSAdriana Kobylak constexpr auto dbusAttrName = "hb_lid_ids"; 34256f538caSAdriana Kobylak constexpr auto dbusAttrType = 34356f538caSAdriana Kobylak "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String"; 34456f538caSAdriana Kobylak 34556f538caSAdriana Kobylak using PendingAttributesType = std::vector<std::pair< 34656f538caSAdriana Kobylak std::string, std::tuple<std::string, std::variant<std::string>>>>; 34756f538caSAdriana Kobylak PendingAttributesType pendingAttributes; 34856f538caSAdriana Kobylak pendingAttributes.emplace_back(std::make_pair( 34956f538caSAdriana Kobylak dbusAttrName, std::make_tuple(dbusAttrType, biosAttrStr))); 35056f538caSAdriana Kobylak 35156f538caSAdriana Kobylak auto bus = sdbusplus::bus::new_default(); 35256f538caSAdriana Kobylak auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 35356f538caSAdriana Kobylak MAPPER_INTERFACE, "GetObject"); 35456f538caSAdriana Kobylak method.append(biosConfigPath, std::vector<std::string>({biosConfigIntf})); 35556f538caSAdriana Kobylak std::vector<std::pair<std::string, std::vector<std::string>>> response; 35656f538caSAdriana Kobylak try 35756f538caSAdriana Kobylak { 35856f538caSAdriana Kobylak auto reply = bus.call(method); 35956f538caSAdriana Kobylak reply.read(response); 36056f538caSAdriana Kobylak if (response.empty()) 36156f538caSAdriana Kobylak { 36256f538caSAdriana Kobylak log<level::ERR>("Error reading mapper response", 36356f538caSAdriana Kobylak entry("PATH=%s", biosConfigPath), 36456f538caSAdriana Kobylak entry("INTERFACE=%s", biosConfigIntf)); 36556f538caSAdriana Kobylak return; 36656f538caSAdriana Kobylak } 36756f538caSAdriana Kobylak auto method = bus.new_method_call((response.begin()->first).c_str(), 36856f538caSAdriana Kobylak biosConfigPath, 36956f538caSAdriana Kobylak SYSTEMD_PROPERTY_INTERFACE, "Set"); 37056f538caSAdriana Kobylak method.append(biosConfigIntf, "PendingAttributes", 37156f538caSAdriana Kobylak std::variant<PendingAttributesType>(pendingAttributes)); 37256f538caSAdriana Kobylak bus.call(method); 37356f538caSAdriana Kobylak } 37456f538caSAdriana Kobylak catch (const sdbusplus::exception::SdBusError& e) 37556f538caSAdriana Kobylak { 37656f538caSAdriana Kobylak log<level::ERR>("Error setting the bios attribute", 37756f538caSAdriana Kobylak entry("ERROR=%s", e.what()), 37856f538caSAdriana Kobylak entry("ATTRIBUTE=%s", dbusAttrName)); 37956f538caSAdriana Kobylak return; 38056f538caSAdriana Kobylak } 38156f538caSAdriana Kobylak } 38253a27395SAdriana Kobylak 38353a27395SAdriana Kobylak /** 384099543e4SBrad Bishop * @brief Make callbacks on 385099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances. 386099543e4SBrad Bishop * 387099543e4SBrad Bishop * Look for an instance of 388099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided 389099543e4SBrad Bishop * argument and if found, issue the provided callback. 390099543e4SBrad Bishop * 391099543e4SBrad Bishop * @param[in] interfacesAndProperties the interfaces in which to look for an 392099543e4SBrad Bishop * instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem 393099543e4SBrad Bishop * @param[in] callback the user callback to make if 394099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in 395099543e4SBrad Bishop * interfacesAndProperties 396099543e4SBrad Bishop * @return true if interfacesAndProperties contained an instance of 397099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise 398099543e4SBrad Bishop */ 399099543e4SBrad Bishop bool maybeCall(const std::map<std::string, 400099543e4SBrad Bishop std::map<std::string, 401099543e4SBrad Bishop std::variant<std::vector<std::string>>>>& 402099543e4SBrad Bishop interfacesAndProperties, 403099543e4SBrad Bishop const MaybeCallCallbackType& callback) 404099543e4SBrad Bishop { 405099543e4SBrad Bishop using namespace std::string_literals; 406099543e4SBrad Bishop 407099543e4SBrad Bishop static const auto interfaceName = 408099543e4SBrad Bishop "xyz.openbmc_project.Configuration.IBMCompatibleSystem"s; 409099543e4SBrad Bishop auto interfaceIterator = interfacesAndProperties.find(interfaceName); 410099543e4SBrad Bishop if (interfaceIterator == interfacesAndProperties.cend()) 411099543e4SBrad Bishop { 412099543e4SBrad Bishop // IBMCompatibleSystem interface not found, so instruct the caller to 413099543e4SBrad Bishop // keep waiting or try again later. 414099543e4SBrad Bishop return false; 415099543e4SBrad Bishop } 416099543e4SBrad Bishop auto propertyIterator = interfaceIterator->second.find("Names"s); 417099543e4SBrad Bishop if (propertyIterator == interfaceIterator->second.cend()) 418099543e4SBrad Bishop { 419099543e4SBrad Bishop // The interface exists but the property doesn't. This is a bug in the 420099543e4SBrad Bishop // IBMCompatibleSystem implementation. The caller should not try 421099543e4SBrad Bishop // again. 422099543e4SBrad Bishop std::cerr << "Names property not implemented on " << interfaceName 423099543e4SBrad Bishop << "\n"; 424099543e4SBrad Bishop return true; 425099543e4SBrad Bishop } 426099543e4SBrad Bishop 427099543e4SBrad Bishop const auto& ibmCompatibleSystem = 428099543e4SBrad Bishop std::get<std::vector<std::string>>(propertyIterator->second); 429099543e4SBrad Bishop if (callback) 430099543e4SBrad Bishop { 431099543e4SBrad Bishop callback(ibmCompatibleSystem); 432099543e4SBrad Bishop } 433099543e4SBrad Bishop 434099543e4SBrad Bishop // IBMCompatibleSystem found and callback issued. 435099543e4SBrad Bishop return true; 436099543e4SBrad Bishop } 437099543e4SBrad Bishop 438099543e4SBrad Bishop /** 439099543e4SBrad Bishop * @brief Make callbacks on 440099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances. 441099543e4SBrad Bishop * 442099543e4SBrad Bishop * Look for an instance of 443099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided 444099543e4SBrad Bishop * argument and if found, issue the provided callback. 445099543e4SBrad Bishop * 446099543e4SBrad Bishop * @param[in] message the DBus message in which to look for an instance of 447099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem 448099543e4SBrad Bishop * @param[in] callback the user callback to make if 449099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in 450099543e4SBrad Bishop * message 451099543e4SBrad Bishop * @return true if message contained an instance of 452099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise 453099543e4SBrad Bishop */ 454099543e4SBrad Bishop bool maybeCallMessage(sdbusplus::message::message& message, 455099543e4SBrad Bishop const MaybeCallCallbackType& callback) 456099543e4SBrad Bishop { 457099543e4SBrad Bishop std::map<std::string, 458099543e4SBrad Bishop std::map<std::string, std::variant<std::vector<std::string>>>> 459099543e4SBrad Bishop interfacesAndProperties; 460099543e4SBrad Bishop sdbusplus::message::object_path _; 461099543e4SBrad Bishop message.read(_, interfacesAndProperties); 462099543e4SBrad Bishop return maybeCall(interfacesAndProperties, callback); 463099543e4SBrad Bishop } 464099543e4SBrad Bishop 465099543e4SBrad Bishop /** 466099543e4SBrad Bishop * @brief Determine system support for host firmware well-known names. 467099543e4SBrad Bishop * 468099543e4SBrad Bishop * Using the provided extensionMap and 469099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if 470099543e4SBrad Bishop * well-known names for host firmare blob files are necessary and if so, create 471099543e4SBrad Bishop * them. 472099543e4SBrad Bishop * 473099543e4SBrad Bishop * @param[in] extensionMap a map of 474099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob 475099543e4SBrad Bishop * file extensions. 476099543e4SBrad Bishop * @param[in] hostFirmwareDirectory The directory in which findLinks should 477099543e4SBrad Bishop * look for host firmware blob files that need well-known names. 478099543e4SBrad Bishop * @param[in] ibmCompatibleSystem The names property of an instance of 479099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem 480099543e4SBrad Bishop * @param[in] errorCallback A callback made in the event of filesystem errors. 481099543e4SBrad Bishop */ 482099543e4SBrad Bishop void maybeMakeLinks( 483099543e4SBrad Bishop const std::map<std::string, std::vector<std::string>>& extensionMap, 484099543e4SBrad Bishop const std::filesystem::path& hostFirmwareDirectory, 485099543e4SBrad Bishop const std::vector<std::string>& ibmCompatibleSystem, 486099543e4SBrad Bishop const ErrorCallbackType& errorCallback) 487099543e4SBrad Bishop { 488099543e4SBrad Bishop std::vector<std::string> extensions; 489099543e4SBrad Bishop if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem, 490099543e4SBrad Bishop extensions)) 491099543e4SBrad Bishop { 492099543e4SBrad Bishop findLinks(hostFirmwareDirectory, extensions, errorCallback, writeLink); 493099543e4SBrad Bishop } 494099543e4SBrad Bishop } 495099543e4SBrad Bishop 496099543e4SBrad Bishop /** 49753a27395SAdriana Kobylak * @brief Determine system support for updating the bios attribute table. 49853a27395SAdriana Kobylak * 49953a27395SAdriana Kobylak * Using the provided extensionMap and 50053a27395SAdriana Kobylak * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if the bios 50153a27395SAdriana Kobylak * attribute table needs to be updated. 50253a27395SAdriana Kobylak * 50353a27395SAdriana Kobylak * @param[in] extensionMap a map of 50453a27395SAdriana Kobylak * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob 50553a27395SAdriana Kobylak * file extensions. 506ae0998f1SAdriana Kobylak * @param[in] elementsJsonFilePath The file path to the json file 50753a27395SAdriana Kobylak * @param[in] ibmCompatibleSystem The names property of an instance of 50853a27395SAdriana Kobylak * xyz.openbmc_project.Configuration.IBMCompatibleSystem 50953a27395SAdriana Kobylak */ 51053a27395SAdriana Kobylak void maybeSetBiosAttr( 51153a27395SAdriana Kobylak const std::map<std::string, std::vector<std::string>>& extensionMap, 512ae0998f1SAdriana Kobylak const std::filesystem::path& elementsJsonFilePath, 51353a27395SAdriana Kobylak const std::vector<std::string>& ibmCompatibleSystem) 51453a27395SAdriana Kobylak { 51553a27395SAdriana Kobylak std::vector<std::string> extensions; 51653a27395SAdriana Kobylak if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem, 51753a27395SAdriana Kobylak extensions)) 51853a27395SAdriana Kobylak { 519ae0998f1SAdriana Kobylak setBiosAttr(elementsJsonFilePath, extensions); 52053a27395SAdriana Kobylak } 52153a27395SAdriana Kobylak } 52253a27395SAdriana Kobylak 52353a27395SAdriana Kobylak /** 524099543e4SBrad Bishop * @brief process host firmware 525099543e4SBrad Bishop * 526099543e4SBrad Bishop * Allocate a callback context and register for DBus.ObjectManager Interfaces 527099543e4SBrad Bishop * added signals from entity manager. 528099543e4SBrad Bishop * 529099543e4SBrad Bishop * Check the current entity manager object tree for a 530099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem instance (entity 531099543e4SBrad Bishop * manager will be dbus activated if it is not running). If one is found, 532099543e4SBrad Bishop * determine if symlinks need to be created and create them. Instruct the 533099543e4SBrad Bishop * program event loop to exit. 534099543e4SBrad Bishop * 535099543e4SBrad Bishop * If no instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is 536099543e4SBrad Bishop * found return the callback context to main, where the program will sleep 537099543e4SBrad Bishop * until the callback is invoked one or more times and instructs the program 538099543e4SBrad Bishop * event loop to exit when 539099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem is added. 540099543e4SBrad Bishop * 541099543e4SBrad Bishop * @param[in] bus a DBus client connection 542099543e4SBrad Bishop * @param[in] extensionMap a map of 543099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob 544099543e4SBrad Bishop * file extensions. 545099543e4SBrad Bishop * @param[in] hostFirmwareDirectory The directory in which processHostFirmware 546099543e4SBrad Bishop * should look for blob files. 547099543e4SBrad Bishop * @param[in] errorCallback A callback made in the event of filesystem errors. 548099543e4SBrad Bishop * @param[in] loop a program event loop 549099543e4SBrad Bishop * @return nullptr if an instance of 550099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found, otherwise a 551099543e4SBrad Bishop * pointer to an sdbusplus match object. 552099543e4SBrad Bishop */ 553099543e4SBrad Bishop std::shared_ptr<void> processHostFirmware( 554099543e4SBrad Bishop sdbusplus::bus::bus& bus, 555099543e4SBrad Bishop std::map<std::string, std::vector<std::string>> extensionMap, 556099543e4SBrad Bishop std::filesystem::path hostFirmwareDirectory, 557099543e4SBrad Bishop ErrorCallbackType errorCallback, sdeventplus::Event& loop) 558099543e4SBrad Bishop { 559099543e4SBrad Bishop // ownership of extensionMap, hostFirmwareDirectory and errorCallback can't 560099543e4SBrad Bishop // be transfered to the match callback because they are needed in the non 561099543e4SBrad Bishop // async part of this function below, so they need to be moved to the heap. 562099543e4SBrad Bishop auto pExtensionMap = 563099543e4SBrad Bishop std::make_shared<decltype(extensionMap)>(std::move(extensionMap)); 564099543e4SBrad Bishop auto pHostFirmwareDirectory = 565099543e4SBrad Bishop std::make_shared<decltype(hostFirmwareDirectory)>( 566099543e4SBrad Bishop std::move(hostFirmwareDirectory)); 567099543e4SBrad Bishop auto pErrorCallback = 568099543e4SBrad Bishop std::make_shared<decltype(errorCallback)>(std::move(errorCallback)); 569099543e4SBrad Bishop 570099543e4SBrad Bishop // register for a callback in case the IBMCompatibleSystem interface has 571099543e4SBrad Bishop // not yet been published by entity manager. 572099543e4SBrad Bishop auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match::match>( 573099543e4SBrad Bishop bus, 574099543e4SBrad Bishop sdbusplus::bus::match::rules::interfacesAdded() + 575099543e4SBrad Bishop sdbusplus::bus::match::rules::sender( 576099543e4SBrad Bishop "xyz.openbmc_project.EntityManager"), 577099543e4SBrad Bishop [pExtensionMap, pHostFirmwareDirectory, pErrorCallback, 578099543e4SBrad Bishop &loop](auto& message) { 579099543e4SBrad Bishop // bind the extension map, host firmware directory, and error 580099543e4SBrad Bishop // callback to the maybeMakeLinks function. 581099543e4SBrad Bishop auto maybeMakeLinksWithArgsBound = 582099543e4SBrad Bishop std::bind(maybeMakeLinks, std::cref(*pExtensionMap), 583099543e4SBrad Bishop std::cref(*pHostFirmwareDirectory), 584099543e4SBrad Bishop std::placeholders::_1, std::cref(*pErrorCallback)); 585099543e4SBrad Bishop 586099543e4SBrad Bishop // if the InterfacesAdded message contains an an instance of 587099543e4SBrad Bishop // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to 588099543e4SBrad Bishop // see if links are necessary on this system and if so, create 589099543e4SBrad Bishop // them. 590099543e4SBrad Bishop if (maybeCallMessage(message, maybeMakeLinksWithArgsBound)) 591099543e4SBrad Bishop { 592099543e4SBrad Bishop // The IBMCompatibleSystem interface was found and the links 593099543e4SBrad Bishop // were created if applicable. Instruct the event loop / 594099543e4SBrad Bishop // subcommand to exit. 595099543e4SBrad Bishop loop.exit(0); 596099543e4SBrad Bishop } 597099543e4SBrad Bishop }); 598099543e4SBrad Bishop 599099543e4SBrad Bishop // now that we'll get a callback in the event of an InterfacesAdded signal 600099543e4SBrad Bishop // (potentially containing 601099543e4SBrad Bishop // xyz.openbmc_project.Configuration.IBMCompatibleSystem), activate entity 602099543e4SBrad Bishop // manager if it isn't running and enumerate its objects 603099543e4SBrad Bishop auto getManagedObjects = bus.new_method_call( 604099543e4SBrad Bishop "xyz.openbmc_project.EntityManager", "/", 605099543e4SBrad Bishop "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 606099543e4SBrad Bishop std::map<std::string, 607099543e4SBrad Bishop std::map<std::string, std::variant<std::vector<std::string>>>> 608099543e4SBrad Bishop interfacesAndProperties; 609099543e4SBrad Bishop std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)> 610099543e4SBrad Bishop objects; 611c79fa915SAdriana Kobylak try 612c79fa915SAdriana Kobylak { 613c79fa915SAdriana Kobylak auto reply = bus.call(getManagedObjects); 614099543e4SBrad Bishop reply.read(objects); 615c79fa915SAdriana Kobylak } 616c79fa915SAdriana Kobylak catch (const sdbusplus::exception::SdBusError& e) 617c79fa915SAdriana Kobylak { 618c79fa915SAdriana Kobylak // Error querying the EntityManager interface. Return the match to have 619c79fa915SAdriana Kobylak // the callback run if/when the interface appears in D-Bus. 620c79fa915SAdriana Kobylak return interfacesAddedMatch; 621c79fa915SAdriana Kobylak } 622099543e4SBrad Bishop 623099543e4SBrad Bishop // bind the extension map, host firmware directory, and error callback to 624099543e4SBrad Bishop // the maybeMakeLinks function. 625099543e4SBrad Bishop auto maybeMakeLinksWithArgsBound = 626099543e4SBrad Bishop std::bind(maybeMakeLinks, std::cref(*pExtensionMap), 627099543e4SBrad Bishop std::cref(*pHostFirmwareDirectory), std::placeholders::_1, 628099543e4SBrad Bishop std::cref(*pErrorCallback)); 629099543e4SBrad Bishop 630099543e4SBrad Bishop for (const auto& pair : objects) 631099543e4SBrad Bishop { 632099543e4SBrad Bishop std::tie(std::ignore, interfacesAndProperties) = pair; 633099543e4SBrad Bishop // if interfacesAndProperties contains an an instance of 634099543e4SBrad Bishop // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to see 635099543e4SBrad Bishop // if links are necessary on this system and if so, create them 636099543e4SBrad Bishop if (maybeCall(interfacesAndProperties, maybeMakeLinksWithArgsBound)) 637099543e4SBrad Bishop { 638099543e4SBrad Bishop // The IBMCompatibleSystem interface is already on the bus and the 639099543e4SBrad Bishop // links were created if applicable. Instruct the event loop to 640099543e4SBrad Bishop // exit. 641099543e4SBrad Bishop loop.exit(0); 642099543e4SBrad Bishop // The match object isn't needed anymore, so destroy it on return. 643099543e4SBrad Bishop return nullptr; 644099543e4SBrad Bishop } 645099543e4SBrad Bishop } 646099543e4SBrad Bishop 647099543e4SBrad Bishop // The IBMCompatibleSystem interface has not yet been published. Move 648099543e4SBrad Bishop // ownership of the match callback to the caller. 649099543e4SBrad Bishop return interfacesAddedMatch; 650099543e4SBrad Bishop } 65153a27395SAdriana Kobylak 65253a27395SAdriana Kobylak /** 65353a27395SAdriana Kobylak * @brief Update the Bios Attribute Table 65453a27395SAdriana Kobylak * 65553a27395SAdriana Kobylak * If an instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is 65653a27395SAdriana Kobylak * found, update the Bios Attribute Table with the appropriate host firmware 65753a27395SAdriana Kobylak * data. 65853a27395SAdriana Kobylak * 65953a27395SAdriana Kobylak * @param[in] bus - D-Bus client connection. 66053a27395SAdriana Kobylak * @param[in] extensionMap - Map of IBMCompatibleSystem names and host firmware 66153a27395SAdriana Kobylak * file extensions. 662ae0998f1SAdriana Kobylak * @param[in] elementsJsonFilePath - The Path to the json file 66353a27395SAdriana Kobylak * @param[in] loop - Program event loop. 66453a27395SAdriana Kobylak * @return nullptr 66553a27395SAdriana Kobylak */ 66653a27395SAdriana Kobylak std::shared_ptr<void> updateBiosAttrTable( 66753a27395SAdriana Kobylak sdbusplus::bus::bus& bus, 66853a27395SAdriana Kobylak std::map<std::string, std::vector<std::string>> extensionMap, 669ae0998f1SAdriana Kobylak std::filesystem::path elementsJsonFilePath, sdeventplus::Event& loop) 67053a27395SAdriana Kobylak { 67153a27395SAdriana Kobylak auto pExtensionMap = 67253a27395SAdriana Kobylak std::make_shared<decltype(extensionMap)>(std::move(extensionMap)); 673ae0998f1SAdriana Kobylak auto pElementsJsonFilePath = 674ae0998f1SAdriana Kobylak std::make_shared<decltype(elementsJsonFilePath)>( 675ae0998f1SAdriana Kobylak std::move(elementsJsonFilePath)); 67653a27395SAdriana Kobylak 677*fd4a6088SAdriana Kobylak // The BIOS attribute table can only be updated if PLDM is running because 678*fd4a6088SAdriana Kobylak // PLDM is the one that exposes this property. Return if it's not running. 679*fd4a6088SAdriana Kobylak constexpr auto pldmPath = "/xyz/openbmc_project/pldm"; 680*fd4a6088SAdriana Kobylak auto pldmObject = getObject(bus, pldmPath); 681*fd4a6088SAdriana Kobylak if (pldmObject.empty()) 682*fd4a6088SAdriana Kobylak { 683*fd4a6088SAdriana Kobylak loop.exit(0); 684*fd4a6088SAdriana Kobylak return nullptr; 685*fd4a6088SAdriana Kobylak } 686*fd4a6088SAdriana Kobylak 68753a27395SAdriana Kobylak auto getManagedObjects = bus.new_method_call( 68853a27395SAdriana Kobylak "xyz.openbmc_project.EntityManager", "/", 68953a27395SAdriana Kobylak "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 69053a27395SAdriana Kobylak std::map<std::string, 69153a27395SAdriana Kobylak std::map<std::string, std::variant<std::vector<std::string>>>> 69253a27395SAdriana Kobylak interfacesAndProperties; 69353a27395SAdriana Kobylak std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)> 69453a27395SAdriana Kobylak objects; 69553a27395SAdriana Kobylak try 69653a27395SAdriana Kobylak { 69753a27395SAdriana Kobylak auto reply = bus.call(getManagedObjects); 69853a27395SAdriana Kobylak reply.read(objects); 69953a27395SAdriana Kobylak } 70053a27395SAdriana Kobylak catch (const sdbusplus::exception::SdBusError& e) 70153a27395SAdriana Kobylak {} 70253a27395SAdriana Kobylak 703ae0998f1SAdriana Kobylak auto maybeSetAttrWithArgsBound = 704ae0998f1SAdriana Kobylak std::bind(maybeSetBiosAttr, std::cref(*pExtensionMap), 705ae0998f1SAdriana Kobylak std::cref(*pElementsJsonFilePath), std::placeholders::_1); 70653a27395SAdriana Kobylak 70753a27395SAdriana Kobylak for (const auto& pair : objects) 70853a27395SAdriana Kobylak { 70953a27395SAdriana Kobylak std::tie(std::ignore, interfacesAndProperties) = pair; 71053a27395SAdriana Kobylak if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound)) 71153a27395SAdriana Kobylak { 71253a27395SAdriana Kobylak break; 71353a27395SAdriana Kobylak } 71453a27395SAdriana Kobylak } 71553a27395SAdriana Kobylak 71653a27395SAdriana Kobylak loop.exit(0); 71753a27395SAdriana Kobylak return nullptr; 71853a27395SAdriana Kobylak } 71953a27395SAdriana Kobylak 720099543e4SBrad Bishop } // namespace process_hostfirmware 721099543e4SBrad Bishop } // namespace functions 722