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 reinitialize the devtree attributes. 57 * In the regular host boot path devtree attribute need to 58 * initialize the default data and also some of the selected 59 * attributes need to preserve with previous boot value. 60 * Preserve attribute list is available BMC pre-defined location. 61 * This function helps to meet the host ipl requirement 62 * related to attribute persistency management for host ipl. 63 * Steps involved 64 * 1. Create attribute data file from devtree r/w version based on 65 * the reinit attribute list file bmc /usr/share/pdata path. 66 * 2. Create temporary devtree file by copying devtree r/o file 67 * 3. Override temporary copy of devtree with attribute data file 68 * from step 1. 69 * 3a. Apply user provided attribute override if present in the 70 * predefined location. 71 * 4. Copy temporary copy devtree to r/w devtree version file. 72 */ 73 74 void reinitDevtree() 75 { 76 using json = nlohmann::json; 77 78 log<level::INFO>("reinitDevtree: started"); 79 80 // All the file operations is done on temporary copy 81 // This is to avoid any file corruption issue during 82 // copy or attribute import path. 83 openpower::util::TemporaryFile tmpDevtreeFile{}; 84 const auto copyOptions = std::filesystem::copy_options::overwrite_existing; 85 auto tmpDevtreePath = tmpDevtreeFile.getPath(); 86 bool tmpReinitDone = false; 87 // To store callouts details in json format as per pel expectation. 88 json jsonCalloutDataList; 89 jsonCalloutDataList = json::array(); 90 91 try 92 { 93 // Check devtree reinit attributes list file is present 94 auto attrFile = fs::path(DEVTREE_REINIT_ATTRS_LIST); 95 if (!fs::exists(attrFile)) 96 { 97 log<level::ERR>( 98 fmt::format( 99 "devtree attribute export list file is not available: ({})", 100 DEVTREE_REINIT_ATTRS_LIST) 101 .c_str()); 102 throw std::runtime_error("reinitDevtree: missing export list file"); 103 } 104 105 // create temporary data file to store the devtree export data 106 openpower::util::TemporaryFile tmpFile{}; 107 108 { 109 // get temporary datafile pointer. 110 FILE_Ptr fpExport(fopen(tmpFile.getPath().c_str(), "w+"), fclose); 111 112 if (fpExport.get() == nullptr) 113 { 114 log<level::ERR>( 115 fmt::format("Temporary data file failed to open: ({})", 116 tmpFile.getPath().c_str()) 117 .c_str()); 118 throw std::runtime_error( 119 "reinitDevtree: failed to open temporaray data file"); 120 } 121 122 // Step 1: export devtree data based on the reinit attribute list. 123 auto ret = 124 dtree_cronus_export(CEC_DEVTREE_RW_PATH, CEC_INFODB_PATH, 125 DEVTREE_REINIT_ATTRS_LIST, fpExport.get()); 126 if (ret) 127 { 128 log<level::ERR>( 129 fmt::format("Failed({}) to collect attribute export data", 130 ret) 131 .c_str()); 132 throw std::runtime_error( 133 "reinitDevtree: dtree_cronus_export function failed"); 134 } 135 } 136 137 // Step 2: Create temporary devtree file by copying devtree r/o version 138 std::filesystem::copy(CEC_DEVTREE_RO_PATH, tmpDevtreePath, copyOptions); 139 140 // get r/o version data file pointer 141 FILE_Ptr fpImport(fopen(tmpFile.getPath().c_str(), "r"), fclose); 142 if (fpImport.get() == nullptr) 143 { 144 log<level::ERR>( 145 fmt::format("import, temporary data file failed to open: ({})", 146 tmpFile.getPath().c_str()) 147 .c_str()); 148 throw std::runtime_error( 149 "reinitDevtree: import, failed to open temporaray data file"); 150 } 151 152 // Step 3: Update Devtree r/w version with data file attribute data. 153 auto ret = dtree_cronus_import(tmpDevtreePath.c_str(), CEC_INFODB_PATH, 154 fpImport.get()); 155 if (ret) 156 { 157 log<level::ERR>( 158 fmt::format("Failed({}) to update attribute data", ret) 159 .c_str()); 160 throw std::runtime_error( 161 "reinitDevtree: dtree_cronus_import function failed"); 162 } 163 // Step 3.a: Apply user provided attribute override data if present. 164 applyAttrOverride(tmpDevtreePath); 165 166 // Temporary file reinit is success. 167 tmpReinitDone = true; 168 } 169 catch (const std::exception& e) 170 { 171 // Any failures during temporary file re-init should create PEL 172 // and continue with current version of devtree file to allow boot. 173 log<level::ERR>( 174 fmt::format("reinitDevtree failed ({})", e.what()).c_str()); 175 json jsonCalloutDataList; 176 jsonCalloutDataList = json::array(); 177 json jsonCalloutData; 178 jsonCalloutData["Procedure"] = "BMC0001"; 179 jsonCalloutData["Priority"] = "M"; 180 jsonCalloutDataList.emplace_back(jsonCalloutData); 181 openpower::pel::createErrorPEL( 182 "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList); 183 } 184 185 // Step 4: Update devtree r/w file 186 try 187 { 188 if (tmpReinitDone) 189 { 190 // Step 4: Copy temporary version devtree file r/w version file. 191 // Any copy failures should results service failure. 192 std::filesystem::copy(tmpDevtreePath, CEC_DEVTREE_RW_PATH, 193 copyOptions); 194 log<level::INFO>("reinitDevtree: completed successfully"); 195 } 196 else 197 { 198 // Attempt boot with genesis mode attribute data. 199 log<level::WARNING>("reinitDevtree: DEVTREE(r/w) initilizing with " 200 "genesis mode attribute data"); 201 std::filesystem::copy(CEC_DEVTREE_RO_PATH, CEC_DEVTREE_RW_PATH, 202 copyOptions); 203 } 204 } 205 catch (const std::exception& e) 206 { 207 // Any failures during update on r/w file is serious error should create 208 // PEL, with code callout. Also failed the service. 209 // and continue with current version of devtree file to allow boot. 210 log<level::ERR>( 211 fmt::format("reinitDevtree r/w version update failed ({})", 212 e.what()) 213 .c_str()); 214 json jsonCalloutDataList; 215 jsonCalloutDataList = json::array(); 216 json jsonCalloutData; 217 jsonCalloutData["Procedure"] = "BMC0001"; 218 jsonCalloutData["Priority"] = "H"; 219 jsonCalloutDataList.emplace_back(jsonCalloutData); 220 openpower::pel::createErrorPEL( 221 "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList); 222 throw; 223 } 224 } 225 226 REGISTER_PROCEDURE("reinitDevtree", reinitDevtree) 227 228 } // namespace phal 229 } // namespace openpower 230