xref: /openbmc/openpower-proc-control/procedures/phal/reinit_devtree.cpp (revision 94fc70cb0b2eb943fe3bd2fbe74bcca466f07531)
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 #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 
16 extern "C"
17 {
18 #include <dtree.h>
19 }
20 
21 namespace openpower
22 {
23 namespace 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 
42 void 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 
194 REGISTER_PROCEDURE("reinitDevtree", reinitDevtree)
195 
196 } // namespace phal
197 } // namespace openpower
198