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 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 std::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 std::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 = std::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 using Severity = 111 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level; 112 113 log<level::INFO>("reinitDevtree: started"); 114 115 // All the file operations is done on temporary copy 116 // This is to avoid any file corruption issue during 117 // copy or attribute import path. 118 openpower::util::TemporaryFile tmpDevtreeFile{}; 119 const auto copyOptions = std::filesystem::copy_options::overwrite_existing; 120 auto tmpDevtreePath = tmpDevtreeFile.getPath(); 121 bool tmpReinitDone = false; 122 // To store callouts details in json format as per pel expectation. 123 json jsonCalloutDataList; 124 jsonCalloutDataList = json::array(); 125 126 try 127 { 128 // Check devtree reinit attributes list file is present 129 auto attrFile = fs::path(DEVTREE_REINIT_ATTRS_LIST); 130 if (!fs::exists(attrFile)) 131 { 132 log<level::ERR>( 133 std::format( 134 "devtree attribute export list file is not available: ({})", 135 DEVTREE_REINIT_ATTRS_LIST) 136 .c_str()); 137 throw std::runtime_error("reinitDevtree: missing export list file"); 138 } 139 140 // create temporary data file to store the devtree export data 141 openpower::util::TemporaryFile tmpFile{}; 142 143 { 144 // get temporary datafile pointer. 145 FILE_Ptr fpExport(fopen(tmpFile.getPath().c_str(), "w+"), fclose); 146 147 if (fpExport.get() == nullptr) 148 { 149 log<level::ERR>( 150 std::format("Temporary data file failed to open: ({})", 151 tmpFile.getPath().c_str()) 152 .c_str()); 153 throw std::runtime_error( 154 "reinitDevtree: failed to open temporaray data file"); 155 } 156 157 // Step 1: export devtree data based on the reinit attribute list. 158 auto ret = dtree_cronus_export(CEC_DEVTREE_RW_PATH, CEC_INFODB_PATH, 159 DEVTREE_REINIT_ATTRS_LIST, 160 fpExport.get()); 161 if (ret) 162 { 163 log<level::ERR>( 164 std::format("Failed({}) to collect attribute export data", 165 ret) 166 .c_str()); 167 throw std::runtime_error( 168 "reinitDevtree: dtree_cronus_export function failed"); 169 } 170 } 171 172 // Step 2: Create temporary devtree file by copying devtree r/o version 173 fs::path roFilePath = computeRODeviceTreePath(); 174 std::filesystem::copy(roFilePath, tmpDevtreePath, copyOptions); 175 176 // get r/o version data file pointer 177 FILE_Ptr fpImport(fopen(tmpFile.getPath().c_str(), "r"), fclose); 178 if (fpImport.get() == nullptr) 179 { 180 log<level::ERR>( 181 std::format("import, temporary data file failed to open: ({})", 182 tmpFile.getPath().c_str()) 183 .c_str()); 184 throw std::runtime_error( 185 "reinitDevtree: import, failed to open temporaray data file"); 186 } 187 188 // Step 3: Update Devtree r/w version with data file attribute data. 189 auto ret = dtree_cronus_import(tmpDevtreePath.c_str(), CEC_INFODB_PATH, 190 fpImport.get()); 191 if (ret) 192 { 193 log<level::ERR>( 194 std::format("Failed({}) to update attribute data", ret) 195 .c_str()); 196 throw std::runtime_error( 197 "reinitDevtree: dtree_cronus_import function failed"); 198 } 199 // Step 3.a: Apply user provided attribute override data if present. 200 applyAttrOverride(tmpDevtreePath); 201 202 // Temporary file reinit is success. 203 tmpReinitDone = true; 204 } 205 catch (const std::exception& e) 206 { 207 // Any failures during temporary file re-init should create PEL 208 // and continue with current version of devtree file to allow boot. 209 log<level::ERR>( 210 std::format("reinitDevtree failed ({})", e.what()).c_str()); 211 json jsonCalloutDataList; 212 jsonCalloutDataList = json::array(); 213 json jsonCalloutData; 214 jsonCalloutData["Procedure"] = "BMC0001"; 215 jsonCalloutData["Priority"] = "M"; 216 jsonCalloutDataList.emplace_back(jsonCalloutData); 217 openpower::pel::createErrorPEL( 218 "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList, {}, 219 Severity::Error); 220 } 221 222 // Step 4: Update devtree r/w file 223 try 224 { 225 if (tmpReinitDone) 226 { 227 // Step 4: Copy temporary version devtree file r/w version file. 228 // Any copy failures should results service failure. 229 std::filesystem::copy(tmpDevtreePath, CEC_DEVTREE_RW_PATH, 230 copyOptions); 231 log<level::INFO>("reinitDevtree: completed successfully"); 232 } 233 else 234 { 235 // Attempt boot with genesis mode attribute data. 236 fs::path roFilePath = computeRODeviceTreePath(); 237 log<level::WARNING>("reinitDevtree: DEVTREE(r/w) initilizing with " 238 "genesis mode attribute data"); 239 std::filesystem::copy(roFilePath, CEC_DEVTREE_RW_PATH, copyOptions); 240 } 241 } 242 catch (const std::exception& e) 243 { 244 // Any failures during update on r/w file is serious error should create 245 // PEL, with code callout. Also failed the service. 246 // and continue with current version of devtree file to allow boot. 247 log<level::ERR>( 248 std::format("reinitDevtree r/w version update failed ({})", 249 e.what()) 250 .c_str()); 251 json jsonCalloutDataList; 252 jsonCalloutDataList = json::array(); 253 json jsonCalloutData; 254 jsonCalloutData["Procedure"] = "BMC0001"; 255 jsonCalloutData["Priority"] = "H"; 256 jsonCalloutDataList.emplace_back(jsonCalloutData); 257 openpower::pel::createErrorPEL( 258 "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList, {}, 259 Severity::Error); 260 throw; 261 } 262 } 263 264 REGISTER_PROCEDURE("reinitDevtree", reinitDevtree) 265 266 } // namespace phal 267 } // namespace openpower 268