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