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 {
operator ()openpower::phal::FileCloser29 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
applyAttrOverride(fs::path & devtreeFile)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 */
computeRODeviceTreePath()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
reinitDevtree()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 =
169 dtree_cronus_export(CEC_DEVTREE_RW_PATH, CEC_INFODB_PATH,
170 DEVTREE_REINIT_ATTRS_LIST, 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