blob: 60e6e49f64e5fd012121d9519e4f10a705900e23 [file] [log] [blame]
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -06001#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
16extern "C"
17{
18#include <dtree.h>
19}
20
21namespace openpower
22{
23namespace phal
24{
Jayanth Othayoth5409e872021-12-03 07:21:44 -060025using namespace phosphor::logging;
26using FILE_Ptr = std::unique_ptr<FILE, decltype(&::fclose)>;
27namespace fs = std::filesystem;
28
29void 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
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -060055/**
Marri Devender Rao90166c12022-01-07 02:22:53 -060056 * @brief Compute RO device tree file path from RW symbolic link
57 * @return RO file path one failure exception will be thrown
58 */
59fs::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/**
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -060089 * @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.
Jayanth Othayoth5409e872021-12-03 07:21:44 -0600102 * 3a. Apply user provided attribute override if present in the
103 * predefined location.
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600104 * 4. Copy temporary copy devtree to r/w devtree version file.
105 */
106
107void reinitDevtree()
108{
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600109 using json = nlohmann::json;
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600110
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
Marri Devender Rao90166c12022-01-07 02:22:53 -0600171 fs::path roFilePath = computeRODeviceTreePath();
172 std::filesystem::copy(roFilePath, tmpDevtreePath, copyOptions);
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600173
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 }
Jayanth Othayoth5409e872021-12-03 07:21:44 -0600197 // Step 3.a: Apply user provided attribute override data if present.
198 applyAttrOverride(tmpDevtreePath);
199
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600200 // 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.
Marri Devender Rao90166c12022-01-07 02:22:53 -0600233 fs::path roFilePath = computeRODeviceTreePath();
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600234 log<level::WARNING>("reinitDevtree: DEVTREE(r/w) initilizing with "
235 "genesis mode attribute data");
Marri Devender Rao90166c12022-01-07 02:22:53 -0600236 std::filesystem::copy(roFilePath, CEC_DEVTREE_RW_PATH, copyOptions);
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600237 }
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
260REGISTER_PROCEDURE("reinitDevtree", reinitDevtree)
261
262} // namespace phal
263} // namespace openpower