xref: /openbmc/openpower-vpd-parser/vpd-tool/src/vpd_tool_main.cpp (revision 963e842d76719f5ca740ba03afabdb7aacea542e)
1 #include "tool_constants.hpp"
2 #include "tool_utils.hpp"
3 #include "vpd_tool.hpp"
4 
5 #include <CLI/CLI.hpp>
6 
7 #include <filesystem>
8 #include <iostream>
9 
10 /**
11  * @brief Resets the VPD on DBus for all the Frus.
12  *
13  * API clears the inventory persisted data and restarts the phosphor inventory
14  * manager(PIM) DBus service and the VPD manager service. VPD manager service
15  * collects the VPD for all the FRU's listed on the system config JSON and calls
16  * PIM to publish VPD on DBus.
17  *
18  * Note: Force reset only happens if chassis is powered off.
19  *
20  * @return On success returns 0, otherwise returns -1.
21  */
forceReset()22 int forceReset()
23 {
24     if (vpd::utils::isChassisPowerOff())
25     {
26         vpd::VpdTool l_vpdToolObj;
27         return l_vpdToolObj.resetVpdOnDbus();
28     }
29 
30     std::cerr
31         << "The chassis power state is not Off. Force reset operation is not allowed."
32         << std::endl;
33     return vpd::constants::FAILURE;
34 }
35 
36 /**
37  * @brief API to perform manufacturing clean.
38  *
39  * @param[in] i_mfgCleanConfirmFlag - Confirmation flag to perform manufacturing
40  * clean.
41  * @param[in] i_syncBiosAttributesFlag - Flag which specifies whether
42  * BIOS attribute related keywords need to be synced from BIOS Config Manager
43  * instead of being reset to default value.
44  *
45  * @return Status returned by cleanSystemVpd operation, success otherwise.
46  */
doMfgClean(const auto & i_mfgCleanConfirmFlag,const auto & i_syncBiosAttributesFlag)47 int doMfgClean(const auto& i_mfgCleanConfirmFlag,
48                const auto& i_syncBiosAttributesFlag)
49 {
50     if (i_mfgCleanConfirmFlag->empty())
51     {
52         constexpr auto MAX_CONFIRMATION_STR_LENGTH{3};
53         std::string l_confirmation{};
54         std::cout
55             << "This option resets some of the system VPD keywords to their default values. Do you really wish to proceed further?[yes/no]:";
56         std::cin >> std::setw(MAX_CONFIRMATION_STR_LENGTH) >> l_confirmation;
57 
58         if (l_confirmation != "yes")
59         {
60             return vpd::constants::SUCCESS;
61         }
62     }
63 
64     vpd::VpdTool l_vpdToolObj;
65 
66     // delete the vpd dump directory
67     l_vpdToolObj.clearVpdDumpDir();
68 
69     return l_vpdToolObj.cleanSystemVpd(!i_syncBiosAttributesFlag->empty());
70 }
71 
72 /**
73  * @brief API to write keyword's value.
74  *
75  * @param[in] i_hardwareFlag - Flag to perform write on hardware.
76  * @param[in] i_keywordValueOption - Option to read keyword value from command.
77  * @param[in] i_vpdPath - DBus object path or EEPROM path.
78  * @param[in] i_recordName - Record to be updated.
79  * @param[in] i_keywordName - Keyword to be updated.
80  * @param[in] i_keywordValue - Value to be updated in keyword.
81  *
82  * @return Status of writeKeyword operation, failure otherwise.
83  */
writeKeyword(const auto & i_hardwareFlag,const auto & i_keywordValueOption,const std::string & i_vpdPath,const std::string & i_recordName,const std::string & i_keywordName,const std::string & i_keywordValue)84 int writeKeyword(const auto& i_hardwareFlag, const auto& i_keywordValueOption,
85                  const std::string& i_vpdPath, const std::string& i_recordName,
86                  const std::string& i_keywordName,
87                  const std::string& i_keywordValue)
88 {
89     std::error_code l_ec;
90 
91     if (!i_hardwareFlag->empty() && !std::filesystem::exists(i_vpdPath, l_ec))
92     {
93         std::cerr << "Given EEPROM file path doesn't exist[" + i_vpdPath << "]."
94                   << std::endl;
95         if (l_ec)
96         {
97             std::cerr << "Reason: " + l_ec.message() << std::endl;
98         }
99         return vpd::constants::FAILURE;
100     }
101 
102     if (!i_keywordValueOption->empty() && i_keywordValue.empty())
103     {
104         std::cerr
105             << "Please provide keyword value.\nUse --value/--file to give "
106                "keyword value. Refer --help."
107             << std::endl;
108         return vpd::constants::FAILURE;
109     }
110 
111     if (i_keywordValueOption->empty())
112     {
113         std::cerr
114             << "Please provide keyword value.\nUse --value/--file to give "
115                "keyword value. Refer --help."
116             << std::endl;
117         return vpd::constants::FAILURE;
118     }
119 
120     if (i_keywordName == vpd::constants::KwdIM)
121     {
122         if (!(i_keywordValue.substr(0, 2).compare("0x") ==
123               vpd::constants::STR_CMP_SUCCESS))
124         {
125             std::cerr << "Please provide IM value in hex format." << std::endl;
126             return vpd::constants::FAILURE;
127         }
128 
129         if (std::find(vpd::constants::validImValues.begin(),
130                       vpd::constants::validImValues.end(), i_keywordValue) ==
131             vpd::constants::validImValues.end())
132         {
133             std::cerr << "Given IM value [" << i_keywordValue
134                       << "] doesn't match with any of the valid system type."
135                       << std::endl;
136             return vpd::constants::FAILURE;
137         }
138     }
139 
140     vpd::VpdTool l_vpdToolObj;
141     return l_vpdToolObj.writeKeyword(i_vpdPath, i_recordName, i_keywordName,
142                                      i_keywordValue, !i_hardwareFlag->empty());
143 }
144 
145 /**
146  * @brief API to read keyword's value.
147  *
148  * @param[in] i_hardwareFlag - Flag to perform write on hardware.
149  * @param[in] i_vpdPath - DBus object path or EEPROM path.
150  * @param[in] i_recordName - Record to be updated.
151  * @param[in] i_keywordName - Keyword to be updated.
152  * @param[in] i_filePath - File path to save keyword's read value.
153  *
154  * @return On success return 0, otherwise return -1.
155  */
readKeyword(const auto & i_hardwareFlag,const std::string & i_vpdPath,const std::string & i_recordName,const std::string & i_keywordName,const std::string & i_filePath)156 int readKeyword(const auto& i_hardwareFlag, const std::string& i_vpdPath,
157                 const std::string& i_recordName,
158                 const std::string& i_keywordName, const std::string& i_filePath)
159 {
160     std::error_code l_ec;
161 
162     if (!i_hardwareFlag->empty() && !std::filesystem::exists(i_vpdPath, l_ec))
163     {
164         std::string l_errMessage{
165             "Given EEPROM file path doesn't exist : " + i_vpdPath};
166 
167         if (l_ec)
168         {
169             l_errMessage += ". filesystem call exists failed, reason: " +
170                             l_ec.message();
171         }
172 
173         std::cerr << l_errMessage << std::endl;
174         return vpd::constants::FAILURE;
175     }
176 
177     bool l_isHardwareOperation = (!i_hardwareFlag->empty() ? true : false);
178 
179     vpd::VpdTool l_vpdToolObj;
180     return l_vpdToolObj.readKeyword(i_vpdPath, i_recordName, i_keywordName,
181                                     l_isHardwareOperation, i_filePath);
182 }
183 
184 /**
185  * @brief API to check option value pair in the tool command.
186  *
187  * In VPD tool command, some of the option(s) mandate values to be passed along
188  * with the option. This API based on option, detects those mandatory value(s).
189  *
190  * @param[in] i_objectOption - Option to pass object path.
191  * @param[in] i_vpdPath - Object path, DBus or EEPROM.
192  * @param[in] i_recordOption - Option to pass record name.
193  * @param[in] i_recordName - Record name.
194  * @param[in] i_keywordOption - Option to pass keyword name.
195  * @param[in] i_keywordName - Keyword name.
196  * @param[in] i_fileOption - Option to pass file path.
197  * @param[in] i_filePath - File path.
198  *
199  * @return Success if corresponding value is found against option, failure
200  * otherwise.
201  */
checkOptionValuePair(const auto & i_objectOption,const auto & i_vpdPath,const auto & i_recordOption,const auto & i_recordName,const auto & i_keywordOption,const auto & i_keywordName,const auto & i_fileOption,const auto & i_filePath)202 int checkOptionValuePair(const auto& i_objectOption, const auto& i_vpdPath,
203                          const auto& i_recordOption, const auto& i_recordName,
204                          const auto& i_keywordOption, const auto& i_keywordName,
205                          const auto& i_fileOption, const auto& i_filePath)
206 {
207     if (!i_objectOption->empty() && i_vpdPath.empty())
208     {
209         std::cout << "Given path is empty." << std::endl;
210         return vpd::constants::FAILURE;
211     }
212 
213     if (!i_recordOption->empty() &&
214         (i_recordName.size() != vpd::constants::RECORD_SIZE))
215     {
216         std::cerr << "Record " << i_recordName << " is not supported."
217                   << std::endl;
218         return vpd::constants::FAILURE;
219     }
220 
221     if (!i_keywordOption->empty() &&
222         (i_keywordName.size() != vpd::constants::KEYWORD_SIZE))
223     {
224         std::cerr << "Keyword " << i_keywordName << " is not supported."
225                   << std::endl;
226         return vpd::constants::FAILURE;
227     }
228 
229     if (!i_fileOption->empty() && i_filePath.empty())
230     {
231         std::cout << "File path is empty." << std::endl;
232         return vpd::constants::FAILURE;
233     }
234 
235     return vpd::constants::SUCCESS;
236 }
237 
238 /**
239  * @brief API to create app footer.
240  *
241  * @param[in] i_app - CLI::App object.
242  */
updateFooter(CLI::App & i_app)243 void updateFooter(CLI::App& i_app)
244 {
245     i_app.footer(
246         "Read:\n"
247         "    IPZ Format:\n"
248         "        From DBus to console: "
249         "vpd-tool -r -O <DBus Object Path> -R <Record Name> -K <Keyword Name>\n"
250         "        From DBus to file: "
251         "vpd-tool -r -O <DBus Object Path> -R <Record Name> -K <Keyword Name> --file <File Path>\n"
252         "        From hardware to console: "
253         "vpd-tool -r -H -O <EEPROM Path> -R <Record Name> -K <Keyword Name>\n"
254         "        From hardware to file: "
255         "vpd-tool -r -H -O <EEPROM Path> -R <Record Name> -K <Keyword Name> --file <File Path>\n"
256         "Write:\n"
257         "    IPZ Format:\n"
258         "        On DBus: "
259         "vpd-tool -w/-u -O <DBus Object Path> -R <Record Name> -K <Keyword Name> -V <Keyword Value>\n"
260         "        On DBus, take keyword value from file:\n"
261         "              vpd-tool -w/-u -O <DBus Object Path> -R <Record Name> -K <Keyword Name> --file <File Path>\n"
262         "        On hardware: "
263         "vpd-tool -w/-u -H -O <EEPROM Path> -R <Record Name> -K <Keyword Name> -V <Keyword Value>\n"
264         "        On hardware, take keyword value from file:\n"
265         "              vpd-tool -w/-u -H -O <EEPROM Path> -R <Record Name> -K <Keyword Name> --file <File Path>\n"
266         "Dump Object:\n"
267         "    From DBus to console: "
268         "vpd-tool -o -O <DBus Object Path>\n"
269         "Fix System VPD:\n"
270         "    vpd-tool --fixSystemVPD\n"
271         "MfgClean:\n"
272         "        Flag to clean and reset specific keywords on system VPD to its default value.\n"
273         "        vpd-tool --mfgClean\n"
274         "        To sync BIOS attribute related keywords with BIOS Config Manager:\n"
275         "        vpd-tool --mfgClean --syncBiosAttributes\n"
276         "Dump Inventory:\n"
277         "   From DBus to console in JSON format: "
278         "vpd-tool -i\n"
279         "   From DBus to console in Table format: "
280         "vpd-tool -i -t\n"
281         "Force Reset:\n"
282         "   vpd-tool --forceReset\n");
283 }
284 
main(int argc,char ** argv)285 int main(int argc, char** argv)
286 {
287     CLI::App l_app{"VPD Command Line Tool"};
288 
289     std::string l_vpdPath{};
290     std::string l_recordName{};
291     std::string l_keywordName{};
292     std::string l_filePath{};
293     std::string l_keywordValue{};
294 
295     updateFooter(l_app);
296 
297     auto l_objectOption =
298         l_app.add_option("--object, -O", l_vpdPath, "File path");
299     auto l_recordOption =
300         l_app.add_option("--record, -R", l_recordName, "Record name");
301     auto l_keywordOption =
302         l_app.add_option("--keyword, -K", l_keywordName, "Keyword name");
303 
304     auto l_fileOption = l_app.add_option(
305         "--file", l_filePath,
306         "Absolute file path,\nNote: For write operation, file should contain keyword’s value in either ascii or in hex format.");
307 
308     auto l_keywordValueOption =
309         l_app.add_option("--value, -V", l_keywordValue,
310                          "Keyword value in ascii/hex format."
311                          " ascii ex: 01234; hex ex: 0x30313233");
312 
313     auto l_hardwareFlag =
314         l_app.add_flag("--Hardware, -H", "CAUTION: Developer only option.");
315 
316     auto l_readFlag = l_app.add_flag("--readKeyword, -r", "Read keyword")
317                           ->needs(l_objectOption)
318                           ->needs(l_recordOption)
319                           ->needs(l_keywordOption);
320 
321     auto l_writeFlag =
322         l_app
323             .add_flag(
324                 "--writeKeyword, -w,--updateKeyword, -u",
325                 "Write keyword,\nNote: In case DBus path is provided, both EEPROM and DBus are updated with the given keyword's value.\nIn case EEPROM path is provided, only the given EEPROM is updated with the given keyword's value.")
326             ->needs(l_objectOption)
327             ->needs(l_recordOption)
328             ->needs(l_keywordOption);
329 
330     // ToDo: Take offset value from user for hardware path.
331 
332     auto l_dumpObjFlag =
333         l_app
334             .add_flag("--dumpObject, -o",
335                       "Dump specific properties of an inventory object")
336             ->needs(l_objectOption);
337 
338     auto l_fixSystemVpdFlag = l_app.add_flag(
339         "--fixSystemVPD",
340         "Use this option to interactively fix critical system VPD keywords");
341     auto l_dumpInventoryFlag =
342         l_app.add_flag("--dumpInventory, -i", "Dump all the inventory objects");
343 
344     auto l_mfgCleanFlag = l_app.add_flag(
345         "--mfgClean", "Manufacturing clean on system VPD keyword");
346 
347     auto l_mfgCleanConfirmFlag = l_app.add_flag(
348         "--yes", "Using this flag with --mfgClean option, assumes "
349                  "yes to proceed without confirmation.");
350 
351     auto l_dumpInventoryTableFlag =
352         l_app.add_flag("--table, -t", "Dump inventory in table format");
353 
354     auto l_mfgCleanSyncBiosAttributesFlag = l_app.add_flag(
355         "--syncBiosAttributes, -s",
356         "Using this flag with --mfgClean option, Syncs the BIOS attribute related keywords from BIOS Config Manager service instead resetting keyword's value to default value");
357 
358     auto l_forceResetFlag = l_app.add_flag(
359         "--forceReset, -f, -F",
360         "Force collect for hardware. CAUTION: Developer only option.");
361 
362     CLI11_PARSE(l_app, argc, argv);
363 
364     if (checkOptionValuePair(l_objectOption, l_vpdPath, l_recordOption,
365                              l_recordName, l_keywordOption, l_keywordName,
366                              l_fileOption, l_filePath) ==
367         vpd::constants::FAILURE)
368     {
369         return vpd::constants::FAILURE;
370     }
371 
372     if (!l_readFlag->empty())
373     {
374         return readKeyword(l_hardwareFlag, l_vpdPath, l_recordName,
375                            l_keywordName, l_filePath);
376     }
377 
378     if (!l_writeFlag->empty())
379     {
380         if ((l_keywordValueOption->empty() && l_fileOption->empty()) ||
381             (!l_keywordValueOption->empty() && !l_fileOption->empty()))
382         {
383             std::cerr
384                 << "Please provide keyword value.\nUse --value/--file to give "
385                    "keyword value. Refer --help."
386                 << std::endl;
387             return vpd::constants::FAILURE;
388         }
389 
390         if (!l_fileOption->empty())
391         {
392             l_keywordValue = vpd::utils::readValueFromFile(l_filePath);
393             if (l_keywordValue.empty())
394             {
395                 return vpd::constants::FAILURE;
396             }
397 
398             return writeKeyword(l_hardwareFlag, l_fileOption, l_vpdPath,
399                                 l_recordName, l_keywordName, l_keywordValue);
400         }
401 
402         return writeKeyword(l_hardwareFlag, l_keywordValueOption, l_vpdPath,
403                             l_recordName, l_keywordName, l_keywordValue);
404     }
405 
406     if (!l_dumpObjFlag->empty())
407     {
408         vpd::VpdTool l_vpdToolObj;
409         return l_vpdToolObj.dumpObject(l_vpdPath);
410     }
411 
412     if (!l_fixSystemVpdFlag->empty())
413     {
414         vpd::VpdTool l_vpdToolObj;
415         return l_vpdToolObj.fixSystemVpd();
416     }
417 
418     if (!l_mfgCleanFlag->empty())
419     {
420         return doMfgClean(l_mfgCleanConfirmFlag,
421                           l_mfgCleanSyncBiosAttributesFlag);
422     }
423 
424     if (!l_dumpInventoryFlag->empty())
425     {
426         vpd::VpdTool l_vpdToolObj;
427         return l_vpdToolObj.dumpInventory(!l_dumpInventoryTableFlag->empty());
428     }
429 
430     if (!l_forceResetFlag->empty())
431     {
432         return forceReset();
433     }
434 
435     std::cout << l_app.help() << std::endl;
436     return vpd::constants::FAILURE;
437 }
438