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