blob: 6451c9242065a9c6cb522b57982a4f8db36de550 [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/**
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.
Jayanth Othayoth5409e872021-12-03 07:21:44 -060069 * 3a. Apply user provided attribute override if present in the
70 * predefined location.
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -060071 * 4. Copy temporary copy devtree to r/w devtree version file.
72 */
73
74void reinitDevtree()
75{
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -060076 using json = nlohmann::json;
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -060077
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 }
Jayanth Othayoth5409e872021-12-03 07:21:44 -0600163 // Step 3.a: Apply user provided attribute override data if present.
164 applyAttrOverride(tmpDevtreePath);
165
Jayanth Othayoth94fc70c2021-11-25 08:18:03 -0600166 // 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
226REGISTER_PROCEDURE("reinitDevtree", reinitDevtree)
227
228} // namespace phal
229} // namespace openpower