#include "config.h" #include "extensions/phal/create_pel.hpp" #include "registration.hpp" #include "temporary_file.hpp" #include #include #include #include #include #include extern "C" { #include } namespace openpower { namespace phal { using namespace phosphor::logging; struct FileCloser { void operator()(FILE* fp) const { fclose(fp); } }; using FILE_Ptr = std::unique_ptr; 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"), FileCloser()); // Update Devtree with attribute override data. auto ret = dtree_cronus_import(devtreeFile.c_str(), CEC_INFODB_PATH, fpOverride.get()); if (ret) { log( std::format("Failed({}) to update attribute override data", ret) .c_str()); throw std::runtime_error( "applyAttrOverride: dtree_cronus_import failed"); } log("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 = std::format("Failed to read the target file " "for the RW device tree symbolic link ({})", CEC_DEVTREE_RW_PATH); log(err.c_str()); throw std::runtime_error(err); } fs::path roFilePath = CEC_DEVTREE_RO_BASE_PATH / rwFileName; if (!fs::exists(roFilePath)) { auto err = std::format("RO device tree file ({}) does not " "exit ", roFilePath.string()); log(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("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( std::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+"), FileCloser()); if (fpExport.get() == nullptr) { log( std::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( std::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"), FileCloser()); if (fpImport.get() == nullptr) { log( std::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( std::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( std::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("reinitDevtree: completed successfully"); } else { // Attempt boot with genesis mode attribute data. fs::path roFilePath = computeRODeviceTreePath(); log("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( std::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