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 9*ae0998f1SAdriana 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> 18*ae0998f1SAdriana 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 /** 35099543e4SBrad Bishop * @brief Issue callbacks safely 36099543e4SBrad Bishop * 37099543e4SBrad Bishop * std::function can be empty, so this wrapper method checks for that prior to 38099543e4SBrad Bishop * calling it to avoid std::bad_function_call 39099543e4SBrad Bishop * 40099543e4SBrad Bishop * @tparam Sig the types of the std::function arguments 41099543e4SBrad Bishop * @tparam Args the deduced argument types 42099543e4SBrad Bishop * @param[in] callback the callback being wrapped 43099543e4SBrad Bishop * @param[in] args the callback arguments 44099543e4SBrad Bishop */ 45099543e4SBrad Bishop template <typename... Sig, typename... Args> 46099543e4SBrad Bishop void makeCallback(const std::function<void(Sig...)>& callback, Args&&... args) 47099543e4SBrad Bishop { 48099543e4SBrad Bishop if (callback) 49099543e4SBrad Bishop { 50099543e4SBrad Bishop callback(std::forward<Args>(args)...); 51099543e4SBrad Bishop } 52099543e4SBrad Bishop } 53099543e4SBrad Bishop 54099543e4SBrad Bishop /** 55099543e4SBrad Bishop * @brief Get file extensions for IBMCompatibleSystem 56099543e4SBrad Bishop * 57099543e4SBrad Bishop * IBM host firmware can be deployed as blobs (files) in a filesystem. Host 58099543e4SBrad Bishop * firmware blobs for different values of 59099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem are packaged with 60099543e4SBrad Bishop * different filename extensions. getExtensionsForIbmCompatibleSystem 61099543e4SBrad Bishop * maintains the mapping from a given value of 62099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem to an array of 63099543e4SBrad Bishop * filename extensions. 64099543e4SBrad Bishop * 65099543e4SBrad Bishop * If a mapping is found getExtensionsForIbmCompatibleSystem returns true and 66099543e4SBrad Bishop * the extensions parameter is reset with the map entry. If no mapping is 67099543e4SBrad Bishop * found getExtensionsForIbmCompatibleSystem returns false and extensions is 68099543e4SBrad Bishop * unmodified. 69099543e4SBrad Bishop * 70099543e4SBrad Bishop * @param[in] extensionMap a map of 71099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob 72099543e4SBrad Bishop * file extensions. 73099543e4SBrad Bishop * @param[in] ibmCompatibleSystem The names property of an instance of 74099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem 75099543e4SBrad Bishop * @param[out] extentions the host firmware blob file extensions 76099543e4SBrad Bishop * @return true if an entry was found, otherwise false 77099543e4SBrad Bishop */ 78099543e4SBrad Bishop bool getExtensionsForIbmCompatibleSystem( 79099543e4SBrad Bishop const std::map<std::string, std::vector<std::string>>& extensionMap, 80099543e4SBrad Bishop const std::vector<std::string>& ibmCompatibleSystem, 81099543e4SBrad Bishop std::vector<std::string>& extensions) 82099543e4SBrad Bishop { 83099543e4SBrad Bishop for (const auto& system : ibmCompatibleSystem) 84099543e4SBrad Bishop { 85099543e4SBrad Bishop auto extensionMapIterator = extensionMap.find(system); 86099543e4SBrad Bishop if (extensionMapIterator != extensionMap.end()) 87099543e4SBrad Bishop { 88099543e4SBrad Bishop extensions = extensionMapIterator->second; 89099543e4SBrad Bishop return true; 90099543e4SBrad Bishop } 91099543e4SBrad Bishop } 92099543e4SBrad Bishop 93099543e4SBrad Bishop return false; 94099543e4SBrad Bishop } 95099543e4SBrad Bishop 96099543e4SBrad Bishop /** 97099543e4SBrad Bishop * @brief Write host firmware well-known name 98099543e4SBrad Bishop * 99099543e4SBrad Bishop * A wrapper around std::filesystem::create_symlink that avoids EEXIST by 100099543e4SBrad Bishop * deleting any pre-existing file. 101099543e4SBrad Bishop * 102099543e4SBrad Bishop * @param[in] linkTarget The link target argument to 103099543e4SBrad Bishop * std::filesystem::create_symlink 104099543e4SBrad Bishop * @param[in] linkPath The link path argument to std::filesystem::create_symlink 105099543e4SBrad Bishop * @param[in] errorCallback A callback made in the event of filesystem errors. 106099543e4SBrad Bishop */ 107099543e4SBrad Bishop void writeLink(const std::filesystem::path& linkTarget, 108099543e4SBrad Bishop const std::filesystem::path& linkPath, 109099543e4SBrad Bishop const ErrorCallbackType& errorCallback) 110099543e4SBrad Bishop { 111099543e4SBrad Bishop std::error_code ec; 112099543e4SBrad Bishop 113099543e4SBrad Bishop // remove files with the same name as the symlink to be created, 114099543e4SBrad Bishop // otherwise symlink will fail with EEXIST. 115099543e4SBrad Bishop if (!std::filesystem::remove(linkPath, ec)) 116099543e4SBrad Bishop { 117099543e4SBrad Bishop if (ec) 118099543e4SBrad Bishop { 119099543e4SBrad Bishop makeCallback(errorCallback, linkPath, ec); 120099543e4SBrad Bishop return; 121099543e4SBrad Bishop } 122099543e4SBrad Bishop } 123099543e4SBrad Bishop 124099543e4SBrad Bishop std::filesystem::create_symlink(linkTarget, linkPath, ec); 125099543e4SBrad Bishop if (ec) 126099543e4SBrad Bishop { 127099543e4SBrad Bishop makeCallback(errorCallback, linkPath, ec); 128099543e4SBrad Bishop return; 129099543e4SBrad Bishop } 130099543e4SBrad Bishop } 131099543e4SBrad Bishop 132099543e4SBrad Bishop /** 133099543e4SBrad Bishop * @brief Find host firmware blob files that need well-known names 134099543e4SBrad Bishop * 135099543e4SBrad Bishop * The IBM host firmware runtime looks for data and/or additional code while 136099543e4SBrad Bishop * bootstraping in files with well-known names. findLinks uses the provided 137099543e4SBrad Bishop * extensions argument to find host firmware blob files that require a 138099543e4SBrad Bishop * well-known name. When a blob is found, issue the provided callback 139099543e4SBrad Bishop * (typically a function that will write a symlink). 140099543e4SBrad Bishop * 141099543e4SBrad Bishop * @param[in] hostFirmwareDirectory The directory in which findLinks should 142099543e4SBrad Bishop * look for host firmware blob files that need well-known names. 143099543e4SBrad Bishop * @param[in] extentions The extensions of the firmware blob files denote a 144099543e4SBrad Bishop * host firmware blob file requires a well-known name. 145099543e4SBrad Bishop * @param[in] errorCallback A callback made in the event of filesystem errors. 146099543e4SBrad Bishop * @param[in] linkCallback A callback made when host firmware blob files 147099543e4SBrad Bishop * needing a well known name are found. 148099543e4SBrad Bishop */ 149099543e4SBrad Bishop void findLinks(const std::filesystem::path& hostFirmwareDirectory, 150099543e4SBrad Bishop const std::vector<std::string>& extensions, 151099543e4SBrad Bishop const ErrorCallbackType& errorCallback, 152099543e4SBrad Bishop const LinkCallbackType& linkCallback) 153099543e4SBrad Bishop { 154099543e4SBrad Bishop std::error_code ec; 155099543e4SBrad Bishop std::filesystem::directory_iterator directoryIterator(hostFirmwareDirectory, 156099543e4SBrad Bishop ec); 157099543e4SBrad Bishop if (ec) 158099543e4SBrad Bishop { 159099543e4SBrad Bishop makeCallback(errorCallback, hostFirmwareDirectory, ec); 160099543e4SBrad Bishop return; 161099543e4SBrad Bishop } 162099543e4SBrad Bishop 163fdc91fa0SAdriana Kobylak // Create a symlink from HBB to the corresponding LID file if it exists 164fdc91fa0SAdriana Kobylak static const auto hbbLid = "81e0065a.lid"; 165fdc91fa0SAdriana Kobylak auto hbbLidPath = hostFirmwareDirectory / hbbLid; 166fdc91fa0SAdriana Kobylak if (std::filesystem::exists(hbbLidPath)) 167fdc91fa0SAdriana Kobylak { 168fdc91fa0SAdriana Kobylak static const auto hbbName = "HBB"; 169fdc91fa0SAdriana Kobylak auto hbbLinkPath = hostFirmwareDirectory / hbbName; 170fdc91fa0SAdriana Kobylak makeCallback(linkCallback, hbbLid, hbbLinkPath, errorCallback); 171fdc91fa0SAdriana Kobylak } 172fdc91fa0SAdriana Kobylak 173099543e4SBrad Bishop for (; directoryIterator != std::filesystem::end(directoryIterator); 174099543e4SBrad Bishop directoryIterator.increment(ec)) 175099543e4SBrad Bishop { 176099543e4SBrad Bishop const auto& file = directoryIterator->path(); 177099543e4SBrad Bishop if (ec) 178099543e4SBrad Bishop { 179099543e4SBrad Bishop makeCallback(errorCallback, file, ec); 180099543e4SBrad Bishop // quit here if the increment call failed otherwise the loop may 181099543e4SBrad Bishop // never finish 182099543e4SBrad Bishop break; 183099543e4SBrad Bishop } 184099543e4SBrad Bishop 185099543e4SBrad Bishop if (std::find(extensions.begin(), extensions.end(), file.extension()) == 186099543e4SBrad Bishop extensions.end()) 187099543e4SBrad Bishop { 188099543e4SBrad Bishop // this file doesn't have an extension or doesn't match any of the 189099543e4SBrad Bishop // provided extensions. 190099543e4SBrad Bishop continue; 191099543e4SBrad Bishop } 192099543e4SBrad Bishop 193099543e4SBrad Bishop auto linkPath(file.parent_path().append( 194099543e4SBrad Bishop static_cast<const std::string&>(file.stem()))); 195099543e4SBrad Bishop 196099543e4SBrad Bishop makeCallback(linkCallback, file.filename(), linkPath, errorCallback); 197099543e4SBrad Bishop } 198099543e4SBrad Bishop } 199099543e4SBrad Bishop 200099543e4SBrad Bishop /** 201*ae0998f1SAdriana Kobylak * @brief Parse the elements json file and construct a string with the data to 202*ae0998f1SAdriana Kobylak * be used to update the bios attribute table. 203*ae0998f1SAdriana Kobylak * 204*ae0998f1SAdriana Kobylak * @param[in] elementsJsonFilePath - The path to the host firmware json file. 205*ae0998f1SAdriana Kobylak * @param[in] extensions - The extensions of the firmware blob files. 20653a27395SAdriana Kobylak */ 207*ae0998f1SAdriana Kobylak std::string getBiosAttrStr(const std::filesystem::path& elementsJsonFilePath, 208*ae0998f1SAdriana Kobylak const std::vector<std::string>& extensions) 20956f538caSAdriana Kobylak { 21056f538caSAdriana Kobylak std::string biosAttrStr{}; 21156f538caSAdriana Kobylak 212*ae0998f1SAdriana Kobylak std::ifstream jsonFile(elementsJsonFilePath.c_str()); 213*ae0998f1SAdriana Kobylak if (!jsonFile) 214*ae0998f1SAdriana Kobylak { 215*ae0998f1SAdriana Kobylak return {}; 216*ae0998f1SAdriana Kobylak } 217*ae0998f1SAdriana Kobylak 218*ae0998f1SAdriana Kobylak std::map<std::string, std::string> attr; 219*ae0998f1SAdriana Kobylak auto data = nlohmann::json::parse(jsonFile, nullptr, false); 220*ae0998f1SAdriana Kobylak if (data.is_discarded()) 221*ae0998f1SAdriana Kobylak { 222*ae0998f1SAdriana Kobylak log<level::ERR>("Error parsing JSON file", 223*ae0998f1SAdriana Kobylak entry("FILE=%s", elementsJsonFilePath.c_str())); 224*ae0998f1SAdriana Kobylak return {}; 225*ae0998f1SAdriana Kobylak } 226*ae0998f1SAdriana Kobylak 227*ae0998f1SAdriana Kobylak // .get requires a non-const iterator 228*ae0998f1SAdriana Kobylak for (auto& iter : data["lids"]) 229*ae0998f1SAdriana Kobylak { 230*ae0998f1SAdriana Kobylak std::string name{}; 231*ae0998f1SAdriana Kobylak std::string lid{}; 232*ae0998f1SAdriana Kobylak 233*ae0998f1SAdriana Kobylak try 234*ae0998f1SAdriana Kobylak { 235*ae0998f1SAdriana Kobylak name = iter["element_name"].get<std::string>(); 236*ae0998f1SAdriana Kobylak lid = iter["short_lid_name"].get<std::string>(); 237*ae0998f1SAdriana Kobylak } 238*ae0998f1SAdriana Kobylak catch (std::exception& e) 239*ae0998f1SAdriana Kobylak { 240*ae0998f1SAdriana Kobylak // Possibly the element or lid name field was not found 241*ae0998f1SAdriana Kobylak log<level::ERR>("Error reading JSON field", 242*ae0998f1SAdriana Kobylak entry("FILE=%s", elementsJsonFilePath.c_str()), 243*ae0998f1SAdriana Kobylak entry("ERROR=%s", e.what())); 244*ae0998f1SAdriana Kobylak continue; 245*ae0998f1SAdriana Kobylak } 246*ae0998f1SAdriana Kobylak 247*ae0998f1SAdriana Kobylak // The elements with the ipl extension have higher priority. Therefore 248*ae0998f1SAdriana Kobylak // Use operator[] to overwrite value if an entry for it already exists. 249*ae0998f1SAdriana Kobylak // Ex: if the JSON contains an entry A.P10 followed by A.P10.iplTime, 250*ae0998f1SAdriana Kobylak // the lid value for the latter one will be overwrite the value of the 251*ae0998f1SAdriana Kobylak // first one. 252*ae0998f1SAdriana Kobylak constexpr auto iplExtension = ".iplTime"; 253*ae0998f1SAdriana Kobylak std::filesystem::path path(name); 254*ae0998f1SAdriana Kobylak if (path.extension() == iplExtension) 255*ae0998f1SAdriana Kobylak { 256*ae0998f1SAdriana Kobylak // Some elements have an additional extension, ex: .P10.iplTime 257*ae0998f1SAdriana Kobylak // Strip off the ipl extension with stem(), then check if there is 258*ae0998f1SAdriana Kobylak // an additional extension with extension(). 259*ae0998f1SAdriana Kobylak if (!path.stem().extension().empty()) 260*ae0998f1SAdriana Kobylak { 261*ae0998f1SAdriana Kobylak // Check if the extension matches the extensions for this system 262*ae0998f1SAdriana Kobylak if (std::find(extensions.begin(), extensions.end(), 263*ae0998f1SAdriana Kobylak path.stem().extension()) == extensions.end()) 264*ae0998f1SAdriana Kobylak { 265*ae0998f1SAdriana Kobylak continue; 266*ae0998f1SAdriana Kobylak } 267*ae0998f1SAdriana Kobylak } 268*ae0998f1SAdriana Kobylak // Get the element name without extensions by calling stem() twice 269*ae0998f1SAdriana Kobylak // since stem() returns the base name if no periods are found. 270*ae0998f1SAdriana Kobylak // Therefore both "element.P10" and "element.P10.iplTime" would 271*ae0998f1SAdriana Kobylak // become "element". 272*ae0998f1SAdriana Kobylak attr[path.stem().stem()] = lid; 273*ae0998f1SAdriana Kobylak continue; 274*ae0998f1SAdriana Kobylak } 275*ae0998f1SAdriana Kobylak 276*ae0998f1SAdriana Kobylak // Process all other extensions. The extension should match the list of 277*ae0998f1SAdriana Kobylak // supported extensions for this system. Use .insert() to only add 278*ae0998f1SAdriana Kobylak // entries that do not exist, so to not overwrite the values that may 279*ae0998f1SAdriana Kobylak // had been added that had the ipl extension. 280*ae0998f1SAdriana Kobylak if (std::find(extensions.begin(), extensions.end(), path.extension()) != 281*ae0998f1SAdriana Kobylak extensions.end()) 282*ae0998f1SAdriana Kobylak { 283*ae0998f1SAdriana Kobylak attr.insert({path.stem(), lid}); 284*ae0998f1SAdriana Kobylak } 285*ae0998f1SAdriana Kobylak } 286*ae0998f1SAdriana Kobylak for (const auto& a : attr) 287*ae0998f1SAdriana Kobylak { 288*ae0998f1SAdriana Kobylak // Build the bios attribute string with format: 289*ae0998f1SAdriana Kobylak // "element1=lid1,element2=lid2,elementN=lidN," 290*ae0998f1SAdriana Kobylak biosAttrStr += a.first + "=" + a.second + ","; 291*ae0998f1SAdriana Kobylak } 292*ae0998f1SAdriana Kobylak 293*ae0998f1SAdriana Kobylak return biosAttrStr; 294*ae0998f1SAdriana Kobylak } 295*ae0998f1SAdriana Kobylak 296*ae0998f1SAdriana Kobylak /** 297*ae0998f1SAdriana Kobylak * @brief Set the bios attribute table with details of the host firmware data 298*ae0998f1SAdriana Kobylak * for this system. 299*ae0998f1SAdriana Kobylak * 300*ae0998f1SAdriana Kobylak * @param[in] elementsJsonFilePath - The path to the host firmware json file. 301*ae0998f1SAdriana Kobylak * @param[in] extentions - The extensions of the firmware blob files. 302*ae0998f1SAdriana Kobylak */ 303*ae0998f1SAdriana Kobylak void setBiosAttr(const std::filesystem::path& elementsJsonFilePath, 304*ae0998f1SAdriana Kobylak const std::vector<std::string>& extensions) 305*ae0998f1SAdriana Kobylak { 306*ae0998f1SAdriana Kobylak auto biosAttrStr = getBiosAttrStr(elementsJsonFilePath, extensions); 307*ae0998f1SAdriana Kobylak 30856f538caSAdriana Kobylak constexpr auto biosConfigPath = "/xyz/openbmc_project/bios_config/manager"; 30956f538caSAdriana Kobylak constexpr auto biosConfigIntf = "xyz.openbmc_project.BIOSConfig.Manager"; 31056f538caSAdriana Kobylak constexpr auto dbusAttrName = "hb_lid_ids"; 31156f538caSAdriana Kobylak constexpr auto dbusAttrType = 31256f538caSAdriana Kobylak "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String"; 31356f538caSAdriana Kobylak 31456f538caSAdriana Kobylak using PendingAttributesType = std::vector<std::pair< 31556f538caSAdriana Kobylak std::string, std::tuple<std::string, std::variant<std::string>>>>; 31656f538caSAdriana Kobylak PendingAttributesType pendingAttributes; 31756f538caSAdriana Kobylak pendingAttributes.emplace_back(std::make_pair( 31856f538caSAdriana Kobylak dbusAttrName, std::make_tuple(dbusAttrType, biosAttrStr))); 31956f538caSAdriana Kobylak 32056f538caSAdriana Kobylak auto bus = sdbusplus::bus::new_default(); 32156f538caSAdriana Kobylak auto method = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 32256f538caSAdriana Kobylak MAPPER_INTERFACE, "GetObject"); 32356f538caSAdriana Kobylak method.append(biosConfigPath, std::vector<std::string>({biosConfigIntf})); 32456f538caSAdriana Kobylak std::vector<std::pair<std::string, std::vector<std::string>>> response; 32556f538caSAdriana Kobylak try 32656f538caSAdriana Kobylak { 32756f538caSAdriana Kobylak auto reply = bus.call(method); 32856f538caSAdriana Kobylak reply.read(response); 32956f538caSAdriana Kobylak if (response.empty()) 33056f538caSAdriana Kobylak { 33156f538caSAdriana Kobylak log<level::ERR>("Error reading mapper response", 33256f538caSAdriana Kobylak entry("PATH=%s", biosConfigPath), 33356f538caSAdriana Kobylak entry("INTERFACE=%s", biosConfigIntf)); 33456f538caSAdriana Kobylak return; 33556f538caSAdriana Kobylak } 33656f538caSAdriana Kobylak auto method = bus.new_method_call((response.begin()->first).c_str(), 33756f538caSAdriana Kobylak biosConfigPath, 33856f538caSAdriana Kobylak SYSTEMD_PROPERTY_INTERFACE, "Set"); 33956f538caSAdriana Kobylak method.append(biosConfigIntf, "PendingAttributes", 34056f538caSAdriana Kobylak std::variant<PendingAttributesType>(pendingAttributes)); 34156f538caSAdriana Kobylak bus.call(method); 34256f538caSAdriana Kobylak } 34356f538caSAdriana Kobylak catch (const sdbusplus::exception::SdBusError& e) 34456f538caSAdriana Kobylak { 34556f538caSAdriana Kobylak log<level::ERR>("Error setting the bios attribute", 34656f538caSAdriana Kobylak entry("ERROR=%s", e.what()), 34756f538caSAdriana Kobylak entry("ATTRIBUTE=%s", dbusAttrName)); 34856f538caSAdriana Kobylak return; 34956f538caSAdriana Kobylak } 35056f538caSAdriana Kobylak } 35153a27395SAdriana Kobylak 35253a27395SAdriana Kobylak /** 353099543e4SBrad Bishop * @brief Make callbacks on 354099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances. 355099543e4SBrad Bishop * 356099543e4SBrad Bishop * Look for an instance of 357099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided 358099543e4SBrad Bishop * argument and if found, issue the provided callback. 359099543e4SBrad Bishop * 360099543e4SBrad Bishop * @param[in] interfacesAndProperties the interfaces in which to look for an 361099543e4SBrad Bishop * instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem 362099543e4SBrad Bishop * @param[in] callback the user callback to make if 363099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in 364099543e4SBrad Bishop * interfacesAndProperties 365099543e4SBrad Bishop * @return true if interfacesAndProperties contained an instance of 366099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise 367099543e4SBrad Bishop */ 368099543e4SBrad Bishop bool maybeCall(const std::map<std::string, 369099543e4SBrad Bishop std::map<std::string, 370099543e4SBrad Bishop std::variant<std::vector<std::string>>>>& 371099543e4SBrad Bishop interfacesAndProperties, 372099543e4SBrad Bishop const MaybeCallCallbackType& callback) 373099543e4SBrad Bishop { 374099543e4SBrad Bishop using namespace std::string_literals; 375099543e4SBrad Bishop 376099543e4SBrad Bishop static const auto interfaceName = 377099543e4SBrad Bishop "xyz.openbmc_project.Configuration.IBMCompatibleSystem"s; 378099543e4SBrad Bishop auto interfaceIterator = interfacesAndProperties.find(interfaceName); 379099543e4SBrad Bishop if (interfaceIterator == interfacesAndProperties.cend()) 380099543e4SBrad Bishop { 381099543e4SBrad Bishop // IBMCompatibleSystem interface not found, so instruct the caller to 382099543e4SBrad Bishop // keep waiting or try again later. 383099543e4SBrad Bishop return false; 384099543e4SBrad Bishop } 385099543e4SBrad Bishop auto propertyIterator = interfaceIterator->second.find("Names"s); 386099543e4SBrad Bishop if (propertyIterator == interfaceIterator->second.cend()) 387099543e4SBrad Bishop { 388099543e4SBrad Bishop // The interface exists but the property doesn't. This is a bug in the 389099543e4SBrad Bishop // IBMCompatibleSystem implementation. The caller should not try 390099543e4SBrad Bishop // again. 391099543e4SBrad Bishop std::cerr << "Names property not implemented on " << interfaceName 392099543e4SBrad Bishop << "\n"; 393099543e4SBrad Bishop return true; 394099543e4SBrad Bishop } 395099543e4SBrad Bishop 396099543e4SBrad Bishop const auto& ibmCompatibleSystem = 397099543e4SBrad Bishop std::get<std::vector<std::string>>(propertyIterator->second); 398099543e4SBrad Bishop if (callback) 399099543e4SBrad Bishop { 400099543e4SBrad Bishop callback(ibmCompatibleSystem); 401099543e4SBrad Bishop } 402099543e4SBrad Bishop 403099543e4SBrad Bishop // IBMCompatibleSystem found and callback issued. 404099543e4SBrad Bishop return true; 405099543e4SBrad Bishop } 406099543e4SBrad Bishop 407099543e4SBrad Bishop /** 408099543e4SBrad Bishop * @brief Make callbacks on 409099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem instances. 410099543e4SBrad Bishop * 411099543e4SBrad Bishop * Look for an instance of 412099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem in the provided 413099543e4SBrad Bishop * argument and if found, issue the provided callback. 414099543e4SBrad Bishop * 415099543e4SBrad Bishop * @param[in] message the DBus message in which to look for an instance of 416099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem 417099543e4SBrad Bishop * @param[in] callback the user callback to make if 418099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found in 419099543e4SBrad Bishop * message 420099543e4SBrad Bishop * @return true if message contained an instance of 421099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem, false otherwise 422099543e4SBrad Bishop */ 423099543e4SBrad Bishop bool maybeCallMessage(sdbusplus::message::message& message, 424099543e4SBrad Bishop const MaybeCallCallbackType& callback) 425099543e4SBrad Bishop { 426099543e4SBrad Bishop std::map<std::string, 427099543e4SBrad Bishop std::map<std::string, std::variant<std::vector<std::string>>>> 428099543e4SBrad Bishop interfacesAndProperties; 429099543e4SBrad Bishop sdbusplus::message::object_path _; 430099543e4SBrad Bishop message.read(_, interfacesAndProperties); 431099543e4SBrad Bishop return maybeCall(interfacesAndProperties, callback); 432099543e4SBrad Bishop } 433099543e4SBrad Bishop 434099543e4SBrad Bishop /** 435099543e4SBrad Bishop * @brief Determine system support for host firmware well-known names. 436099543e4SBrad Bishop * 437099543e4SBrad Bishop * Using the provided extensionMap and 438099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if 439099543e4SBrad Bishop * well-known names for host firmare blob files are necessary and if so, create 440099543e4SBrad Bishop * them. 441099543e4SBrad Bishop * 442099543e4SBrad Bishop * @param[in] extensionMap a map of 443099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob 444099543e4SBrad Bishop * file extensions. 445099543e4SBrad Bishop * @param[in] hostFirmwareDirectory The directory in which findLinks should 446099543e4SBrad Bishop * look for host firmware blob files that need well-known names. 447099543e4SBrad Bishop * @param[in] ibmCompatibleSystem The names property of an instance of 448099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem 449099543e4SBrad Bishop * @param[in] errorCallback A callback made in the event of filesystem errors. 450099543e4SBrad Bishop */ 451099543e4SBrad Bishop void maybeMakeLinks( 452099543e4SBrad Bishop const std::map<std::string, std::vector<std::string>>& extensionMap, 453099543e4SBrad Bishop const std::filesystem::path& hostFirmwareDirectory, 454099543e4SBrad Bishop const std::vector<std::string>& ibmCompatibleSystem, 455099543e4SBrad Bishop const ErrorCallbackType& errorCallback) 456099543e4SBrad Bishop { 457099543e4SBrad Bishop std::vector<std::string> extensions; 458099543e4SBrad Bishop if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem, 459099543e4SBrad Bishop extensions)) 460099543e4SBrad Bishop { 461099543e4SBrad Bishop findLinks(hostFirmwareDirectory, extensions, errorCallback, writeLink); 462099543e4SBrad Bishop } 463099543e4SBrad Bishop } 464099543e4SBrad Bishop 465099543e4SBrad Bishop /** 46653a27395SAdriana Kobylak * @brief Determine system support for updating the bios attribute table. 46753a27395SAdriana Kobylak * 46853a27395SAdriana Kobylak * Using the provided extensionMap and 46953a27395SAdriana Kobylak * xyz.openbmc_project.Configuration.IBMCompatibleSystem, determine if the bios 47053a27395SAdriana Kobylak * attribute table needs to be updated. 47153a27395SAdriana Kobylak * 47253a27395SAdriana Kobylak * @param[in] extensionMap a map of 47353a27395SAdriana Kobylak * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob 47453a27395SAdriana Kobylak * file extensions. 475*ae0998f1SAdriana Kobylak * @param[in] elementsJsonFilePath The file path to the json file 47653a27395SAdriana Kobylak * @param[in] ibmCompatibleSystem The names property of an instance of 47753a27395SAdriana Kobylak * xyz.openbmc_project.Configuration.IBMCompatibleSystem 47853a27395SAdriana Kobylak */ 47953a27395SAdriana Kobylak void maybeSetBiosAttr( 48053a27395SAdriana Kobylak const std::map<std::string, std::vector<std::string>>& extensionMap, 481*ae0998f1SAdriana Kobylak const std::filesystem::path& elementsJsonFilePath, 48253a27395SAdriana Kobylak const std::vector<std::string>& ibmCompatibleSystem) 48353a27395SAdriana Kobylak { 48453a27395SAdriana Kobylak std::vector<std::string> extensions; 48553a27395SAdriana Kobylak if (getExtensionsForIbmCompatibleSystem(extensionMap, ibmCompatibleSystem, 48653a27395SAdriana Kobylak extensions)) 48753a27395SAdriana Kobylak { 488*ae0998f1SAdriana Kobylak setBiosAttr(elementsJsonFilePath, extensions); 48953a27395SAdriana Kobylak } 49053a27395SAdriana Kobylak } 49153a27395SAdriana Kobylak 49253a27395SAdriana Kobylak /** 493099543e4SBrad Bishop * @brief process host firmware 494099543e4SBrad Bishop * 495099543e4SBrad Bishop * Allocate a callback context and register for DBus.ObjectManager Interfaces 496099543e4SBrad Bishop * added signals from entity manager. 497099543e4SBrad Bishop * 498099543e4SBrad Bishop * Check the current entity manager object tree for a 499099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem instance (entity 500099543e4SBrad Bishop * manager will be dbus activated if it is not running). If one is found, 501099543e4SBrad Bishop * determine if symlinks need to be created and create them. Instruct the 502099543e4SBrad Bishop * program event loop to exit. 503099543e4SBrad Bishop * 504099543e4SBrad Bishop * If no instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is 505099543e4SBrad Bishop * found return the callback context to main, where the program will sleep 506099543e4SBrad Bishop * until the callback is invoked one or more times and instructs the program 507099543e4SBrad Bishop * event loop to exit when 508099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem is added. 509099543e4SBrad Bishop * 510099543e4SBrad Bishop * @param[in] bus a DBus client connection 511099543e4SBrad Bishop * @param[in] extensionMap a map of 512099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem to host firmware blob 513099543e4SBrad Bishop * file extensions. 514099543e4SBrad Bishop * @param[in] hostFirmwareDirectory The directory in which processHostFirmware 515099543e4SBrad Bishop * should look for blob files. 516099543e4SBrad Bishop * @param[in] errorCallback A callback made in the event of filesystem errors. 517099543e4SBrad Bishop * @param[in] loop a program event loop 518099543e4SBrad Bishop * @return nullptr if an instance of 519099543e4SBrad Bishop * xyz.openbmc_project.Configuration.IBMCompatibleSystem is found, otherwise a 520099543e4SBrad Bishop * pointer to an sdbusplus match object. 521099543e4SBrad Bishop */ 522099543e4SBrad Bishop std::shared_ptr<void> processHostFirmware( 523099543e4SBrad Bishop sdbusplus::bus::bus& bus, 524099543e4SBrad Bishop std::map<std::string, std::vector<std::string>> extensionMap, 525099543e4SBrad Bishop std::filesystem::path hostFirmwareDirectory, 526099543e4SBrad Bishop ErrorCallbackType errorCallback, sdeventplus::Event& loop) 527099543e4SBrad Bishop { 528099543e4SBrad Bishop // ownership of extensionMap, hostFirmwareDirectory and errorCallback can't 529099543e4SBrad Bishop // be transfered to the match callback because they are needed in the non 530099543e4SBrad Bishop // async part of this function below, so they need to be moved to the heap. 531099543e4SBrad Bishop auto pExtensionMap = 532099543e4SBrad Bishop std::make_shared<decltype(extensionMap)>(std::move(extensionMap)); 533099543e4SBrad Bishop auto pHostFirmwareDirectory = 534099543e4SBrad Bishop std::make_shared<decltype(hostFirmwareDirectory)>( 535099543e4SBrad Bishop std::move(hostFirmwareDirectory)); 536099543e4SBrad Bishop auto pErrorCallback = 537099543e4SBrad Bishop std::make_shared<decltype(errorCallback)>(std::move(errorCallback)); 538099543e4SBrad Bishop 539099543e4SBrad Bishop // register for a callback in case the IBMCompatibleSystem interface has 540099543e4SBrad Bishop // not yet been published by entity manager. 541099543e4SBrad Bishop auto interfacesAddedMatch = std::make_shared<sdbusplus::bus::match::match>( 542099543e4SBrad Bishop bus, 543099543e4SBrad Bishop sdbusplus::bus::match::rules::interfacesAdded() + 544099543e4SBrad Bishop sdbusplus::bus::match::rules::sender( 545099543e4SBrad Bishop "xyz.openbmc_project.EntityManager"), 546099543e4SBrad Bishop [pExtensionMap, pHostFirmwareDirectory, pErrorCallback, 547099543e4SBrad Bishop &loop](auto& message) { 548099543e4SBrad Bishop // bind the extension map, host firmware directory, and error 549099543e4SBrad Bishop // callback to the maybeMakeLinks function. 550099543e4SBrad Bishop auto maybeMakeLinksWithArgsBound = 551099543e4SBrad Bishop std::bind(maybeMakeLinks, std::cref(*pExtensionMap), 552099543e4SBrad Bishop std::cref(*pHostFirmwareDirectory), 553099543e4SBrad Bishop std::placeholders::_1, std::cref(*pErrorCallback)); 554099543e4SBrad Bishop 555099543e4SBrad Bishop // if the InterfacesAdded message contains an an instance of 556099543e4SBrad Bishop // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to 557099543e4SBrad Bishop // see if links are necessary on this system and if so, create 558099543e4SBrad Bishop // them. 559099543e4SBrad Bishop if (maybeCallMessage(message, maybeMakeLinksWithArgsBound)) 560099543e4SBrad Bishop { 561099543e4SBrad Bishop // The IBMCompatibleSystem interface was found and the links 562099543e4SBrad Bishop // were created if applicable. Instruct the event loop / 563099543e4SBrad Bishop // subcommand to exit. 564099543e4SBrad Bishop loop.exit(0); 565099543e4SBrad Bishop } 566099543e4SBrad Bishop }); 567099543e4SBrad Bishop 568099543e4SBrad Bishop // now that we'll get a callback in the event of an InterfacesAdded signal 569099543e4SBrad Bishop // (potentially containing 570099543e4SBrad Bishop // xyz.openbmc_project.Configuration.IBMCompatibleSystem), activate entity 571099543e4SBrad Bishop // manager if it isn't running and enumerate its objects 572099543e4SBrad Bishop auto getManagedObjects = bus.new_method_call( 573099543e4SBrad Bishop "xyz.openbmc_project.EntityManager", "/", 574099543e4SBrad Bishop "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 575099543e4SBrad Bishop std::map<std::string, 576099543e4SBrad Bishop std::map<std::string, std::variant<std::vector<std::string>>>> 577099543e4SBrad Bishop interfacesAndProperties; 578099543e4SBrad Bishop std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)> 579099543e4SBrad Bishop objects; 580c79fa915SAdriana Kobylak try 581c79fa915SAdriana Kobylak { 582c79fa915SAdriana Kobylak auto reply = bus.call(getManagedObjects); 583099543e4SBrad Bishop reply.read(objects); 584c79fa915SAdriana Kobylak } 585c79fa915SAdriana Kobylak catch (const sdbusplus::exception::SdBusError& e) 586c79fa915SAdriana Kobylak { 587c79fa915SAdriana Kobylak // Error querying the EntityManager interface. Return the match to have 588c79fa915SAdriana Kobylak // the callback run if/when the interface appears in D-Bus. 589c79fa915SAdriana Kobylak return interfacesAddedMatch; 590c79fa915SAdriana Kobylak } 591099543e4SBrad Bishop 592099543e4SBrad Bishop // bind the extension map, host firmware directory, and error callback to 593099543e4SBrad Bishop // the maybeMakeLinks function. 594099543e4SBrad Bishop auto maybeMakeLinksWithArgsBound = 595099543e4SBrad Bishop std::bind(maybeMakeLinks, std::cref(*pExtensionMap), 596099543e4SBrad Bishop std::cref(*pHostFirmwareDirectory), std::placeholders::_1, 597099543e4SBrad Bishop std::cref(*pErrorCallback)); 598099543e4SBrad Bishop 599099543e4SBrad Bishop for (const auto& pair : objects) 600099543e4SBrad Bishop { 601099543e4SBrad Bishop std::tie(std::ignore, interfacesAndProperties) = pair; 602099543e4SBrad Bishop // if interfacesAndProperties contains an an instance of 603099543e4SBrad Bishop // xyz.openbmc_project.Configuration.IBMCompatibleSystem, check to see 604099543e4SBrad Bishop // if links are necessary on this system and if so, create them 605099543e4SBrad Bishop if (maybeCall(interfacesAndProperties, maybeMakeLinksWithArgsBound)) 606099543e4SBrad Bishop { 607099543e4SBrad Bishop // The IBMCompatibleSystem interface is already on the bus and the 608099543e4SBrad Bishop // links were created if applicable. Instruct the event loop to 609099543e4SBrad Bishop // exit. 610099543e4SBrad Bishop loop.exit(0); 611099543e4SBrad Bishop // The match object isn't needed anymore, so destroy it on return. 612099543e4SBrad Bishop return nullptr; 613099543e4SBrad Bishop } 614099543e4SBrad Bishop } 615099543e4SBrad Bishop 616099543e4SBrad Bishop // The IBMCompatibleSystem interface has not yet been published. Move 617099543e4SBrad Bishop // ownership of the match callback to the caller. 618099543e4SBrad Bishop return interfacesAddedMatch; 619099543e4SBrad Bishop } 62053a27395SAdriana Kobylak 62153a27395SAdriana Kobylak /** 62253a27395SAdriana Kobylak * @brief Update the Bios Attribute Table 62353a27395SAdriana Kobylak * 62453a27395SAdriana Kobylak * If an instance of xyz.openbmc_project.Configuration.IBMCompatibleSystem is 62553a27395SAdriana Kobylak * found, update the Bios Attribute Table with the appropriate host firmware 62653a27395SAdriana Kobylak * data. 62753a27395SAdriana Kobylak * 62853a27395SAdriana Kobylak * @param[in] bus - D-Bus client connection. 62953a27395SAdriana Kobylak * @param[in] extensionMap - Map of IBMCompatibleSystem names and host firmware 63053a27395SAdriana Kobylak * file extensions. 631*ae0998f1SAdriana Kobylak * @param[in] elementsJsonFilePath - The Path to the json file 63253a27395SAdriana Kobylak * @param[in] loop - Program event loop. 63353a27395SAdriana Kobylak * @return nullptr 63453a27395SAdriana Kobylak */ 63553a27395SAdriana Kobylak std::shared_ptr<void> updateBiosAttrTable( 63653a27395SAdriana Kobylak sdbusplus::bus::bus& bus, 63753a27395SAdriana Kobylak std::map<std::string, std::vector<std::string>> extensionMap, 638*ae0998f1SAdriana Kobylak std::filesystem::path elementsJsonFilePath, sdeventplus::Event& loop) 63953a27395SAdriana Kobylak { 64053a27395SAdriana Kobylak auto pExtensionMap = 64153a27395SAdriana Kobylak std::make_shared<decltype(extensionMap)>(std::move(extensionMap)); 642*ae0998f1SAdriana Kobylak auto pElementsJsonFilePath = 643*ae0998f1SAdriana Kobylak std::make_shared<decltype(elementsJsonFilePath)>( 644*ae0998f1SAdriana Kobylak std::move(elementsJsonFilePath)); 64553a27395SAdriana Kobylak 64653a27395SAdriana Kobylak auto getManagedObjects = bus.new_method_call( 64753a27395SAdriana Kobylak "xyz.openbmc_project.EntityManager", "/", 64853a27395SAdriana Kobylak "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 64953a27395SAdriana Kobylak std::map<std::string, 65053a27395SAdriana Kobylak std::map<std::string, std::variant<std::vector<std::string>>>> 65153a27395SAdriana Kobylak interfacesAndProperties; 65253a27395SAdriana Kobylak std::map<sdbusplus::message::object_path, decltype(interfacesAndProperties)> 65353a27395SAdriana Kobylak objects; 65453a27395SAdriana Kobylak try 65553a27395SAdriana Kobylak { 65653a27395SAdriana Kobylak auto reply = bus.call(getManagedObjects); 65753a27395SAdriana Kobylak reply.read(objects); 65853a27395SAdriana Kobylak } 65953a27395SAdriana Kobylak catch (const sdbusplus::exception::SdBusError& e) 66053a27395SAdriana Kobylak {} 66153a27395SAdriana Kobylak 662*ae0998f1SAdriana Kobylak auto maybeSetAttrWithArgsBound = 663*ae0998f1SAdriana Kobylak std::bind(maybeSetBiosAttr, std::cref(*pExtensionMap), 664*ae0998f1SAdriana Kobylak std::cref(*pElementsJsonFilePath), std::placeholders::_1); 66553a27395SAdriana Kobylak 66653a27395SAdriana Kobylak for (const auto& pair : objects) 66753a27395SAdriana Kobylak { 66853a27395SAdriana Kobylak std::tie(std::ignore, interfacesAndProperties) = pair; 66953a27395SAdriana Kobylak if (maybeCall(interfacesAndProperties, maybeSetAttrWithArgsBound)) 67053a27395SAdriana Kobylak { 67153a27395SAdriana Kobylak break; 67253a27395SAdriana Kobylak } 67353a27395SAdriana Kobylak } 67453a27395SAdriana Kobylak 67553a27395SAdriana Kobylak loop.exit(0); 67653a27395SAdriana Kobylak return nullptr; 67753a27395SAdriana Kobylak } 67853a27395SAdriana Kobylak 679099543e4SBrad Bishop } // namespace process_hostfirmware 680099543e4SBrad Bishop } // namespace functions 681