1 #include "config.h" 2 3 #include "extensions/phal/create_pel.hpp" 4 #include "registration.hpp" 5 #include "temporary_file.hpp" 6 7 #include <fcntl.h> 8 #include <fmt/format.h> 9 10 #include <nlohmann/json.hpp> 11 #include <phosphor-logging/elog-errors.hpp> 12 13 #include <cstdio> 14 #include <filesystem> 15 16 extern "C" 17 { 18 #include <dtree.h> 19 } 20 21 namespace openpower 22 { 23 namespace phal 24 { 25 /** 26 * @brief reinitialize the devtree attributes. 27 * In the regular host boot path devtree attribute need to 28 * initialize the default data and also some of the selected 29 * attributes need to preserve with previous boot value. 30 * Preserve attribute list is available BMC pre-defined location. 31 * This function helps to meet the host ipl requirement 32 * related to attribute persistency management for host ipl. 33 * Steps involved 34 * 1. Create attribute data file from devtree r/w version based on 35 * the reinit attribute list file bmc /usr/share/pdata path. 36 * 2. Create temporary devtree file by copying devtree r/o file 37 * 3. Override temporary copy of devtree with attribute data file 38 * from step 1. 39 * 4. Copy temporary copy devtree to r/w devtree version file. 40 */ 41 42 void reinitDevtree() 43 { 44 using namespace phosphor::logging; 45 using FILE_Ptr = std::unique_ptr<FILE, decltype(&::fclose)>; 46 using json = nlohmann::json; 47 namespace fs = std::filesystem; 48 49 log<level::INFO>("reinitDevtree: started"); 50 51 // All the file operations is done on temporary copy 52 // This is to avoid any file corruption issue during 53 // copy or attribute import path. 54 openpower::util::TemporaryFile tmpDevtreeFile{}; 55 const auto copyOptions = std::filesystem::copy_options::overwrite_existing; 56 auto tmpDevtreePath = tmpDevtreeFile.getPath(); 57 bool tmpReinitDone = false; 58 // To store callouts details in json format as per pel expectation. 59 json jsonCalloutDataList; 60 jsonCalloutDataList = json::array(); 61 62 try 63 { 64 // Check devtree reinit attributes list file is present 65 auto attrFile = fs::path(DEVTREE_REINIT_ATTRS_LIST); 66 if (!fs::exists(attrFile)) 67 { 68 log<level::ERR>( 69 fmt::format( 70 "devtree attribute export list file is not available: ({})", 71 DEVTREE_REINIT_ATTRS_LIST) 72 .c_str()); 73 throw std::runtime_error("reinitDevtree: missing export list file"); 74 } 75 76 // create temporary data file to store the devtree export data 77 openpower::util::TemporaryFile tmpFile{}; 78 79 { 80 // get temporary datafile pointer. 81 FILE_Ptr fpExport(fopen(tmpFile.getPath().c_str(), "w+"), fclose); 82 83 if (fpExport.get() == nullptr) 84 { 85 log<level::ERR>( 86 fmt::format("Temporary data file failed to open: ({})", 87 tmpFile.getPath().c_str()) 88 .c_str()); 89 throw std::runtime_error( 90 "reinitDevtree: failed to open temporaray data file"); 91 } 92 93 // Step 1: export devtree data based on the reinit attribute list. 94 auto ret = 95 dtree_cronus_export(CEC_DEVTREE_RW_PATH, CEC_INFODB_PATH, 96 DEVTREE_REINIT_ATTRS_LIST, fpExport.get()); 97 if (ret) 98 { 99 log<level::ERR>( 100 fmt::format("Failed({}) to collect attribute export data", 101 ret) 102 .c_str()); 103 throw std::runtime_error( 104 "reinitDevtree: dtree_cronus_export function failed"); 105 } 106 } 107 108 // Step 2: Create temporary devtree file by copying devtree r/o version 109 std::filesystem::copy(CEC_DEVTREE_RO_PATH, tmpDevtreePath, copyOptions); 110 111 // get r/o version data file pointer 112 FILE_Ptr fpImport(fopen(tmpFile.getPath().c_str(), "r"), fclose); 113 if (fpImport.get() == nullptr) 114 { 115 log<level::ERR>( 116 fmt::format("import, temporary data file failed to open: ({})", 117 tmpFile.getPath().c_str()) 118 .c_str()); 119 throw std::runtime_error( 120 "reinitDevtree: import, failed to open temporaray data file"); 121 } 122 123 // Step 3: Update Devtree r/w version with data file attribute data. 124 auto ret = dtree_cronus_import(tmpDevtreePath.c_str(), CEC_INFODB_PATH, 125 fpImport.get()); 126 if (ret) 127 { 128 log<level::ERR>( 129 fmt::format("Failed({}) to update attribute data", ret) 130 .c_str()); 131 throw std::runtime_error( 132 "reinitDevtree: dtree_cronus_import function failed"); 133 } 134 // Temporary file reinit is success. 135 tmpReinitDone = true; 136 } 137 catch (const std::exception& e) 138 { 139 // Any failures during temporary file re-init should create PEL 140 // and continue with current version of devtree file to allow boot. 141 log<level::ERR>( 142 fmt::format("reinitDevtree failed ({})", e.what()).c_str()); 143 json jsonCalloutDataList; 144 jsonCalloutDataList = json::array(); 145 json jsonCalloutData; 146 jsonCalloutData["Procedure"] = "BMC0001"; 147 jsonCalloutData["Priority"] = "M"; 148 jsonCalloutDataList.emplace_back(jsonCalloutData); 149 openpower::pel::createErrorPEL( 150 "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList); 151 } 152 153 // Step 4: Update devtree r/w file 154 try 155 { 156 if (tmpReinitDone) 157 { 158 // Step 4: Copy temporary version devtree file r/w version file. 159 // Any copy failures should results service failure. 160 std::filesystem::copy(tmpDevtreePath, CEC_DEVTREE_RW_PATH, 161 copyOptions); 162 log<level::INFO>("reinitDevtree: completed successfully"); 163 } 164 else 165 { 166 // Attempt boot with genesis mode attribute data. 167 log<level::WARNING>("reinitDevtree: DEVTREE(r/w) initilizing with " 168 "genesis mode attribute data"); 169 std::filesystem::copy(CEC_DEVTREE_RO_PATH, CEC_DEVTREE_RW_PATH, 170 copyOptions); 171 } 172 } 173 catch (const std::exception& e) 174 { 175 // Any failures during update on r/w file is serious error should create 176 // PEL, with code callout. Also failed the service. 177 // and continue with current version of devtree file to allow boot. 178 log<level::ERR>( 179 fmt::format("reinitDevtree r/w version update failed ({})", 180 e.what()) 181 .c_str()); 182 json jsonCalloutDataList; 183 jsonCalloutDataList = json::array(); 184 json jsonCalloutData; 185 jsonCalloutData["Procedure"] = "BMC0001"; 186 jsonCalloutData["Priority"] = "H"; 187 jsonCalloutDataList.emplace_back(jsonCalloutData); 188 openpower::pel::createErrorPEL( 189 "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList); 190 throw; 191 } 192 } 193 194 REGISTER_PROCEDURE("reinitDevtree", reinitDevtree) 195 196 } // namespace phal 197 } // namespace openpower 198