| #include "config.h" | 
 |  | 
 | #include "extensions/phal/create_pel.hpp" | 
 | #include "registration.hpp" | 
 | #include "temporary_file.hpp" | 
 |  | 
 | #include <fcntl.h> | 
 | #include <fmt/format.h> | 
 |  | 
 | #include <nlohmann/json.hpp> | 
 | #include <phosphor-logging/elog-errors.hpp> | 
 |  | 
 | #include <cstdio> | 
 | #include <filesystem> | 
 |  | 
 | extern "C" | 
 | { | 
 | #include <dtree.h> | 
 | } | 
 |  | 
 | namespace openpower | 
 | { | 
 | namespace phal | 
 | { | 
 | using namespace phosphor::logging; | 
 | using FILE_Ptr = std::unique_ptr<FILE, decltype(&::fclose)>; | 
 | namespace fs = std::filesystem; | 
 |  | 
 | void applyAttrOverride(fs::path& devtreeFile) | 
 | { | 
 |     constexpr auto DEVTREE_ATTR_OVERRIDE_PATH = "/tmp/devtree_attr_override"; | 
 |     auto overrideFile = fs::path(DEVTREE_ATTR_OVERRIDE_PATH); | 
 |     if (!fs::exists(overrideFile)) | 
 |     { | 
 |         return; | 
 |     } | 
 |  | 
 |     // Open attribute override file in r/o mode | 
 |     FILE_Ptr fpOverride(fopen(DEVTREE_ATTR_OVERRIDE_PATH, "r"), fclose); | 
 |  | 
 |     // Update Devtree with attribute override data. | 
 |     auto ret = dtree_cronus_import(devtreeFile.c_str(), CEC_INFODB_PATH, | 
 |                                    fpOverride.get()); | 
 |     if (ret) | 
 |     { | 
 |         log<level::ERR>( | 
 |             fmt::format("Failed({}) to update attribute override data", ret) | 
 |                 .c_str()); | 
 |         throw std::runtime_error( | 
 |             "applyAttrOverride: dtree_cronus_import failed"); | 
 |     } | 
 |     log<level::INFO>("DEVTREE: Applied attribute override data"); | 
 | } | 
 |  | 
 | /** | 
 |  * @brief Compute RO device tree file path from RW symbolic link | 
 |  * @return RO file path one failure exception will be thrown | 
 |  */ | 
 | fs::path computeRODeviceTreePath() | 
 | { | 
 |     // Symbolic links are not created for RO files, compute the lid name | 
 |     // for the RW symbolic link and use it to compute RO file. | 
 |     // Example: | 
 |     // RW file = /media/hostfw/running/DEVTREE -> 81e00672.lid | 
 |     // RO file = /media/hostfw/running-ro/ + 81e00672.lid | 
 |     fs::path rwFileName = fs::read_symlink(CEC_DEVTREE_RW_PATH); | 
 |     if (rwFileName.empty()) | 
 |     { | 
 |         std::string err = | 
 |             fmt::format("Failed to read the target file " | 
 |                         "for the RW device tree symbolic link ({})", | 
 |                         CEC_DEVTREE_RW_PATH); | 
 |         log<level::ERR>(err.c_str()); | 
 |         throw std::runtime_error(err); | 
 |     } | 
 |     fs::path roFilePath = CEC_DEVTREE_RO_BASE_PATH / rwFileName; | 
 |     if (!fs::exists(roFilePath)) | 
 |     { | 
 |         auto err = fmt::format("RO device tree file ({}) does not " | 
 |                                "exit ", | 
 |                                roFilePath.string()); | 
 |         log<level::ERR>(err.c_str()); | 
 |         throw std::runtime_error(err); | 
 |     } | 
 |     return roFilePath; | 
 | } | 
 |  | 
 | /** | 
 |  * @brief reinitialize the devtree attributes. | 
 |  * In the regular host boot path devtree attribute need to | 
 |  * initialize the default data and also some of the selected | 
 |  * attributes need to preserve with previous boot value. | 
 |  * Preserve attribute list is available BMC pre-defined location. | 
 |  * This function helps to meet the host ipl requirement | 
 |  * related to attribute persistency management for host ipl. | 
 |  * Steps involved | 
 |  * 1. Create attribute data file from devtree r/w version based on | 
 |  *    the reinit attribute list file bmc /usr/share/pdata path. | 
 |  * 2. Create temporary devtree file by copying devtree r/o file | 
 |  * 3. Override temporary copy of devtree with attribute data file | 
 |  *    from step 1. | 
 |  * 3a. Apply user provided attribute override if present in the | 
 |  *     predefined location. | 
 |  * 4. Copy  temporary copy devtree to r/w devtree version file. | 
 |  */ | 
 |  | 
 | void reinitDevtree() | 
 | { | 
 |     using json = nlohmann::json; | 
 |     using Severity = | 
 |         sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level; | 
 |  | 
 |     log<level::INFO>("reinitDevtree: started"); | 
 |  | 
 |     // All the file operations is done on temporary copy | 
 |     // This is to avoid any file corruption issue during | 
 |     // copy or attribute import path. | 
 |     openpower::util::TemporaryFile tmpDevtreeFile{}; | 
 |     const auto copyOptions = std::filesystem::copy_options::overwrite_existing; | 
 |     auto tmpDevtreePath = tmpDevtreeFile.getPath(); | 
 |     bool tmpReinitDone = false; | 
 |     // To store callouts details in json format as per pel expectation. | 
 |     json jsonCalloutDataList; | 
 |     jsonCalloutDataList = json::array(); | 
 |  | 
 |     try | 
 |     { | 
 |         // Check devtree reinit attributes list file is present | 
 |         auto attrFile = fs::path(DEVTREE_REINIT_ATTRS_LIST); | 
 |         if (!fs::exists(attrFile)) | 
 |         { | 
 |             log<level::ERR>( | 
 |                 fmt::format( | 
 |                     "devtree attribute export list file is not available: ({})", | 
 |                     DEVTREE_REINIT_ATTRS_LIST) | 
 |                     .c_str()); | 
 |             throw std::runtime_error("reinitDevtree: missing export list file"); | 
 |         } | 
 |  | 
 |         // create temporary data file to store the devtree export data | 
 |         openpower::util::TemporaryFile tmpFile{}; | 
 |  | 
 |         { | 
 |             // get temporary datafile pointer. | 
 |             FILE_Ptr fpExport(fopen(tmpFile.getPath().c_str(), "w+"), fclose); | 
 |  | 
 |             if (fpExport.get() == nullptr) | 
 |             { | 
 |                 log<level::ERR>( | 
 |                     fmt::format("Temporary data file failed to open: ({})", | 
 |                                 tmpFile.getPath().c_str()) | 
 |                         .c_str()); | 
 |                 throw std::runtime_error( | 
 |                     "reinitDevtree: failed to open temporaray data file"); | 
 |             } | 
 |  | 
 |             // Step 1: export devtree data based on the reinit attribute list. | 
 |             auto ret = dtree_cronus_export(CEC_DEVTREE_RW_PATH, CEC_INFODB_PATH, | 
 |                                            DEVTREE_REINIT_ATTRS_LIST, | 
 |                                            fpExport.get()); | 
 |             if (ret) | 
 |             { | 
 |                 log<level::ERR>( | 
 |                     fmt::format("Failed({}) to collect attribute export data", | 
 |                                 ret) | 
 |                         .c_str()); | 
 |                 throw std::runtime_error( | 
 |                     "reinitDevtree: dtree_cronus_export function failed"); | 
 |             } | 
 |         } | 
 |  | 
 |         // Step 2: Create temporary devtree file by copying devtree r/o version | 
 |         fs::path roFilePath = computeRODeviceTreePath(); | 
 |         std::filesystem::copy(roFilePath, tmpDevtreePath, copyOptions); | 
 |  | 
 |         // get r/o version data file pointer | 
 |         FILE_Ptr fpImport(fopen(tmpFile.getPath().c_str(), "r"), fclose); | 
 |         if (fpImport.get() == nullptr) | 
 |         { | 
 |             log<level::ERR>( | 
 |                 fmt::format("import, temporary data file failed to open: ({})", | 
 |                             tmpFile.getPath().c_str()) | 
 |                     .c_str()); | 
 |             throw std::runtime_error( | 
 |                 "reinitDevtree: import, failed to open temporaray data file"); | 
 |         } | 
 |  | 
 |         // Step 3: Update Devtree r/w version with data file attribute data. | 
 |         auto ret = dtree_cronus_import(tmpDevtreePath.c_str(), CEC_INFODB_PATH, | 
 |                                        fpImport.get()); | 
 |         if (ret) | 
 |         { | 
 |             log<level::ERR>( | 
 |                 fmt::format("Failed({}) to update attribute data", ret) | 
 |                     .c_str()); | 
 |             throw std::runtime_error( | 
 |                 "reinitDevtree: dtree_cronus_import function failed"); | 
 |         } | 
 |         // Step 3.a: Apply user provided attribute override data if present. | 
 |         applyAttrOverride(tmpDevtreePath); | 
 |  | 
 |         // Temporary file reinit is success. | 
 |         tmpReinitDone = true; | 
 |     } | 
 |     catch (const std::exception& e) | 
 |     { | 
 |         // Any failures during temporary file re-init should create PEL | 
 |         // and continue with current version of devtree file to allow boot. | 
 |         log<level::ERR>( | 
 |             fmt::format("reinitDevtree failed ({})", e.what()).c_str()); | 
 |         json jsonCalloutDataList; | 
 |         jsonCalloutDataList = json::array(); | 
 |         json jsonCalloutData; | 
 |         jsonCalloutData["Procedure"] = "BMC0001"; | 
 |         jsonCalloutData["Priority"] = "M"; | 
 |         jsonCalloutDataList.emplace_back(jsonCalloutData); | 
 |         openpower::pel::createErrorPEL( | 
 |             "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList, {}, | 
 |             Severity::Error); | 
 |     } | 
 |  | 
 |     // Step 4: Update devtree r/w file | 
 |     try | 
 |     { | 
 |         if (tmpReinitDone) | 
 |         { | 
 |             // Step 4: Copy temporary version devtree file r/w version file. | 
 |             // Any copy failures should results service failure. | 
 |             std::filesystem::copy(tmpDevtreePath, CEC_DEVTREE_RW_PATH, | 
 |                                   copyOptions); | 
 |             log<level::INFO>("reinitDevtree: completed successfully"); | 
 |         } | 
 |         else | 
 |         { | 
 |             // Attempt boot with genesis mode attribute data. | 
 |             fs::path roFilePath = computeRODeviceTreePath(); | 
 |             log<level::WARNING>("reinitDevtree: DEVTREE(r/w) initilizing with " | 
 |                                 "genesis mode attribute data"); | 
 |             std::filesystem::copy(roFilePath, CEC_DEVTREE_RW_PATH, copyOptions); | 
 |         } | 
 |     } | 
 |     catch (const std::exception& e) | 
 |     { | 
 |         // Any failures during update on r/w file is serious error should create | 
 |         // PEL, with code callout. Also failed the service. | 
 |         // and continue with current version of devtree file to allow boot. | 
 |         log<level::ERR>( | 
 |             fmt::format("reinitDevtree r/w version update failed ({})", | 
 |                         e.what()) | 
 |                 .c_str()); | 
 |         json jsonCalloutDataList; | 
 |         jsonCalloutDataList = json::array(); | 
 |         json jsonCalloutData; | 
 |         jsonCalloutData["Procedure"] = "BMC0001"; | 
 |         jsonCalloutData["Priority"] = "H"; | 
 |         jsonCalloutDataList.emplace_back(jsonCalloutData); | 
 |         openpower::pel::createErrorPEL( | 
 |             "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList, {}, | 
 |             Severity::Error); | 
 |         throw; | 
 |     } | 
 | } | 
 |  | 
 | REGISTER_PROCEDURE("reinitDevtree", reinitDevtree) | 
 |  | 
 | } // namespace phal | 
 | } // namespace openpower |