blob: c5ae058a6368c0e17f2118758982f03795fc78d5 [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;
Marri Devender Rao4d5b5bf2022-05-23 09:23:31 -0500110 using Severity =
111 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level;
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600112
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 fmt::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 fmt::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 =
159 dtree_cronus_export(CEC_DEVTREE_RW_PATH, CEC_INFODB_PATH,
160 DEVTREE_REINIT_ATTRS_LIST, fpExport.get());
161 if (ret)
162 {
163 log<level::ERR>(
164 fmt::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
Marri Devender Rao90166c12022-01-07 02:22:53 -0600173 fs::path roFilePath = computeRODeviceTreePath();
174 std::filesystem::copy(roFilePath, tmpDevtreePath, copyOptions);
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600175
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 fmt::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 fmt::format("Failed({}) to update attribute data", ret)
195 .c_str());
196 throw std::runtime_error(
197 "reinitDevtree: dtree_cronus_import function failed");
198 }
Jayanth Othayoth5409e872021-12-03 07:21:44 -0600199 // Step 3.a: Apply user provided attribute override data if present.
200 applyAttrOverride(tmpDevtreePath);
201
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600202 // 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 fmt::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(
Marri Devender Rao4d5b5bf2022-05-23 09:23:31 -0500218 "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList, {},
219 Severity::Error);
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600220 }
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.
Marri Devender Rao90166c12022-01-07 02:22:53 -0600236 fs::path roFilePath = computeRODeviceTreePath();
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600237 log<level::WARNING>("reinitDevtree: DEVTREE(r/w) initilizing with "
238 "genesis mode attribute data");
Marri Devender Rao90166c12022-01-07 02:22:53 -0600239 std::filesystem::copy(roFilePath, CEC_DEVTREE_RW_PATH, copyOptions);
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600240 }
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 fmt::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(
Marri Devender Rao4d5b5bf2022-05-23 09:23:31 -0500258 "org.open_power.PHAL.Error.devtreeReinit", jsonCalloutDataList, {},
259 Severity::Error);
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600260 throw;
261 }
262}
263
264REGISTER_PROCEDURE("reinitDevtree", reinitDevtree)
265
266} // namespace phal
267} // namespace openpower