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 using namespace phosphor::logging; 26 using FILE_Ptr = std::unique_ptr<FILE, decltype(&::fclose)>; 27 namespace fs = std::filesystem; 28 29 void applyAttrOverride(fs::path& devtreeFile) 30 { 31 constexpr auto DEVTREE_ATTR_OVERRIDE_PATH = "/tmp/devtree_attr_override"; 32 auto overrideFile = fs::path(DEVTREE_ATTR_OVERRIDE_PATH); 33 if (!fs::exists(overrideFile)) 34 { 35 return; 36 } 37 38 // Open attribute override file in r/o mode 39 FILE_Ptr fpOverride(fopen(DEVTREE_ATTR_OVERRIDE_PATH, "r"), fclose); 40 41 // Update Devtree with attribute override data. 42 auto ret = dtree_cronus_import(devtreeFile.c_str(), CEC_INFODB_PATH, 43 fpOverride.get()); 44 if (ret) 45 { 46 log<level::ERR>( 47 fmt::format("Failed({}) to update attribute override data", ret) 48 .c_str()); 49 throw std::runtime_error( 50 "applyAttrOverride: dtree_cronus_import failed"); 51 } 52 log<level::INFO>("DEVTREE: Applied attribute override data"); 53 } 54 55 /** 56 * @brief Compute RO device tree file path from RW symbolic link 57 * @return RO file path one failure exception will be thrown 58 */ 59 fs::path computeRODeviceTreePath() 60 { 61 // Symbolic links are not created for RO files, compute the lid name 62 // for the RW symbolic link and use it to compute RO file. 63 // Example: 64 // RW file = /media/hostfw/running/DEVTREE -> 81e00672.lid 65 // RO file = /media/hostfw/running-ro/ + 81e00672.lid 66 fs::path rwFileName = fs::read_symlink(CEC_DEVTREE_RW_PATH); 67 if (rwFileName.empty()) 68 { 69 std::string err = 70 fmt::format("Failed to read the target file " 71 "for the RW device tree symbolic link ({})", 72 CEC_DEVTREE_RW_PATH); 73 log<level::ERR>(err.c_str()); 74 throw std::runtime_error(err); 75 } 76 fs::path roFilePath = CEC_DEVTREE_RO_BASE_PATH / rwFileName; 77 if (!fs::exists(roFilePath)) 78 { 79 auto err = fmt::format("RO device tree file ({}) does not " 80 "exit ", 81 roFilePath.string()); 82 log<level::ERR>(err.c_str()); 83 throw std::runtime_error(err); 84 } 85 return roFilePath; 86 } 87 88 /** 89 * @brief reinitialize the devtree attributes. 90 * In the regular host boot path devtree attribute need to 91 * initialize the default data and also some of the selected 92 * attributes need to preserve with previous boot value. 93 * Preserve attribute list is available BMC pre-defined location. 94 * This function helps to meet the host ipl requirement 95 * related to attribute persistency management for host ipl. 96 * Steps involved 97 * 1. Create attribute data file from devtree r/w version based on 98 * the reinit attribute list file bmc /usr/share/pdata path. 99 * 2. Create temporary devtree file by copying devtree r/o file 100 * 3. Override temporary copy of devtree with attribute data file 101 * from step 1. 102 * 3a. Apply user provided attribute override if present in the 103 * predefined location. 104 * 4. Copy temporary copy devtree to r/w devtree version file. 105 */ 106 107 void reinitDevtree() 108 { 109 using json = nlohmann::json; 110 111 log<level::INFO>("reinitDevtree: started"); 112 113 // All the file operations is done on temporary copy 114 // This is to avoid any file corruption issue during 115 // copy or attribute import path. 116 openpower::util::TemporaryFile tmpDevtreeFile{}; 117 const auto copyOptions = std::filesystem::copy_options::overwrite_existing; 118 auto tmpDevtreePath = tmpDevtreeFile.getPath(); 119 bool tmpReinitDone = false; 120 // To store callouts details in json format as per pel expectation. 121 json jsonCalloutDataList; 122 jsonCalloutDataList = json::array(); 123 124 try 125 { 126 // Check devtree reinit attributes list file is present 127 auto attrFile = fs::path(DEVTREE_REINIT_ATTRS_LIST); 128 if (!fs::exists(attrFile)) 129 { 130 log<level::ERR>( 131 fmt::format( 132 "devtree attribute export list file is not available: ({})", 133 DEVTREE_REINIT_ATTRS_LIST) 134 .c_str()); 135 throw std::runtime_error("reinitDevtree: missing export list file"); 136 } 137 138 // create temporary data file to store the devtree export data 139 openpower::util::TemporaryFile tmpFile{}; 140 141 { 142 // get temporary datafile pointer. 143 FILE_Ptr fpExport(fopen(tmpFile.getPath().c_str(), "w+"), fclose); 144 145 if (fpExport.get() == nullptr) 146 { 147 log<level::ERR>( 148 fmt::format("Temporary data file failed to open: ({})", 149 tmpFile.getPath().c_str()) 150 .c_str()); 151 throw std::runtime_error( 152 "reinitDevtree: failed to open temporaray data file"); 153 } 154 155 // Step 1: export devtree data based on the reinit attribute list. 156 auto ret = 157 dtree_cronus_export(CEC_DEVTREE_RW_PATH, CEC_INFODB_PATH, 158 DEVTREE_REINIT_ATTRS_LIST, fpExport.get()); 159 if (ret) 160 { 161 log<level::ERR>( 162 fmt::format("Failed({}) to collect attribute export data", 163 ret) 164 .c_str()); 165 throw std::runtime_error( 166 "reinitDevtree: dtree_cronus_export function failed"); 167 } 168 } 169 170 // Step 2: Create temporary devtree file by copying devtree r/o version 171 fs::path roFilePath = computeRODeviceTreePath(); 172 std::filesystem::copy(roFilePath, tmpDevtreePath, copyOptions); 173 174 // get r/o version data file pointer 175 FILE_Ptr fpImport(fopen(tmpFile.getPath().c_str(), "r"), fclose); 176 if (fpImport.get() == nullptr) 177 { 178 log<level::ERR>( 179 fmt::format("import, temporary data file failed to open: ({})", 180 tmpFile.getPath().c_str()) 181 .c_str()); 182 throw std::runtime_error( 183 "reinitDevtree: import, failed to open temporaray data file"); 184 } 185 186 // Step 3: Update Devtree r/w version with data file attribute data. 187 auto ret = dtree_cronus_import(tmpDevtreePath.c_str(), CEC_INFODB_PATH, 188 fpImport.get()); 189 if (ret) 190 { 191 log<level::ERR>( 192 fmt::format("Failed({}) to update attribute data", ret) 193 .c_str()); 194 throw std::runtime_error( 195 "reinitDevtree: dtree_cronus_import function failed"); 196 } 197 // Step 3.a: Apply user provided attribute override data if present. 198 applyAttrOverride(tmpDevtreePath); 199 200 // Temporary file reinit is success. 201 tmpReinitDone = true; 202 } 203 catch (const std::exception& e) 204 { 205 // Any failures during temporary file re-init should create PEL 206 // and continue with current version of devtree file to allow boot. 207 log<level::ERR>( 208 fmt::format("reinitDevtree failed ({})", e.what()).c_str()); 209 json jsonCalloutDataList; 210 jsonCalloutDataList = json::array(); 211 json jsonCalloutData; 212 jsonCalloutData["Procedure"] = "BMC0001"; 213 jsonCalloutData["Priority"] = "M"; 214 jsonCalloutDataList.emplace_back(jsonCalloutData); 215 openpower::pel::createErrorPEL( 216 "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList); 217 } 218 219 // Step 4: Update devtree r/w file 220 try 221 { 222 if (tmpReinitDone) 223 { 224 // Step 4: Copy temporary version devtree file r/w version file. 225 // Any copy failures should results service failure. 226 std::filesystem::copy(tmpDevtreePath, CEC_DEVTREE_RW_PATH, 227 copyOptions); 228 log<level::INFO>("reinitDevtree: completed successfully"); 229 } 230 else 231 { 232 // Attempt boot with genesis mode attribute data. 233 fs::path roFilePath = computeRODeviceTreePath(); 234 log<level::WARNING>("reinitDevtree: DEVTREE(r/w) initilizing with " 235 "genesis mode attribute data"); 236 std::filesystem::copy(roFilePath, CEC_DEVTREE_RW_PATH, copyOptions); 237 } 238 } 239 catch (const std::exception& e) 240 { 241 // Any failures during update on r/w file is serious error should create 242 // PEL, with code callout. Also failed the service. 243 // and continue with current version of devtree file to allow boot. 244 log<level::ERR>( 245 fmt::format("reinitDevtree r/w version update failed ({})", 246 e.what()) 247 .c_str()); 248 json jsonCalloutDataList; 249 jsonCalloutDataList = json::array(); 250 json jsonCalloutData; 251 jsonCalloutData["Procedure"] = "BMC0001"; 252 jsonCalloutData["Priority"] = "H"; 253 jsonCalloutDataList.emplace_back(jsonCalloutData); 254 openpower::pel::createErrorPEL( 255 "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList); 256 throw; 257 } 258 } 259 260 REGISTER_PROCEDURE("reinitDevtree", reinitDevtree) 261 262 } // namespace phal 263 } // namespace openpower 264