1 #include "config.h"
2 
3 #include "defines.hpp"
4 #include "ipz_parser.hpp"
5 #include "keyword_vpd_parser.hpp"
6 #include "memory_vpd_parser.hpp"
7 #include "parser_factory.hpp"
8 #include "utils.hpp"
9 #include "vpd_exceptions.hpp"
10 
11 #include <ctype.h>
12 
13 #include <CLI/CLI.hpp>
14 #include <algorithm>
15 #include <cstdarg>
16 #include <exception>
17 #include <filesystem>
18 #include <fstream>
19 #include <gpiod.hpp>
20 #include <iostream>
21 #include <iterator>
22 #include <nlohmann/json.hpp>
23 #include <phosphor-logging/log.hpp>
24 
25 using namespace std;
26 using namespace openpower::vpd;
27 using namespace CLI;
28 using namespace vpd::keyword::parser;
29 using namespace openpower::vpd::constants;
30 namespace fs = filesystem;
31 using json = nlohmann::json;
32 using namespace openpower::vpd::parser::factory;
33 using namespace openpower::vpd::inventory;
34 using namespace openpower::vpd::memory::parser;
35 using namespace openpower::vpd::parser::interface;
36 using namespace openpower::vpd::exceptions;
37 using namespace phosphor::logging;
38 
39 static const deviceTreeMap deviceTreeSystemTypeMap = {
40     {RAINIER_2U, "conf@aspeed-bmc-ibm-rainier.dtb"},
41     {RAINIER_4U, "conf@aspeed-bmc-ibm-rainier-4u.dtb"},
42     {EVEREST, "conf@aspeed-bmc-ibm-everest.dtb"}};
43 
44 /**
45  * @brief Returns the power state for chassis0
46  */
47 static auto getPowerState()
48 {
49     // TODO: How do we handle multiple chassis?
50     string powerState{};
51     auto bus = sdbusplus::bus::new_default();
52     auto properties =
53         bus.new_method_call("xyz.openbmc_project.State.Chassis",
54                             "/xyz/openbmc_project/state/chassis0",
55                             "org.freedesktop.DBus.Properties", "Get");
56     properties.append("xyz.openbmc_project.State.Chassis");
57     properties.append("CurrentPowerState");
58     auto result = bus.call(properties);
59     if (!result.is_method_error())
60     {
61         variant<string> val;
62         result.read(val);
63         if (auto pVal = get_if<string>(&val))
64         {
65             powerState = *pVal;
66         }
67     }
68     cout << "Power state is: " << powerState << endl;
69     return powerState;
70 }
71 
72 /**
73  * @brief Expands location codes
74  */
75 static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
76                                bool isSystemVpd)
77 {
78     auto expanded{unexpanded};
79     static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
80     static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
81     static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
82     size_t idx = expanded.find("fcs");
83     try
84     {
85         if (idx != string::npos)
86         {
87             string fc{};
88             string se{};
89             if (isSystemVpd)
90             {
91                 const auto& fcData = vpdMap.at("VCEN").at("FC");
92                 const auto& seData = vpdMap.at("VCEN").at("SE");
93                 fc = string(fcData.data(), fcData.size());
94                 se = string(seData.data(), seData.size());
95             }
96             else
97             {
98                 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
99                 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
100             }
101 
102             // TODO: See if ND1 can be placed in the JSON
103             expanded.replace(idx, 3, fc.substr(0, 4) + ".ND1." + se);
104         }
105         else
106         {
107             idx = expanded.find("mts");
108             if (idx != string::npos)
109             {
110                 string mt{};
111                 string se{};
112                 if (isSystemVpd)
113                 {
114                     const auto& mtData = vpdMap.at("VSYS").at("TM");
115                     const auto& seData = vpdMap.at("VSYS").at("SE");
116                     mt = string(mtData.data(), mtData.size());
117                     se = string(seData.data(), seData.size());
118                 }
119                 else
120                 {
121                     mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
122                     se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
123                 }
124 
125                 replace(mt.begin(), mt.end(), '-', '.');
126                 expanded.replace(idx, 3, mt + "." + se);
127             }
128         }
129     }
130     catch (exception& e)
131     {
132         cerr << "Failed to expand location code with exception: " << e.what()
133              << "\n";
134     }
135     return expanded;
136 }
137 
138 /**
139  * @brief Populate FRU specific interfaces.
140  *
141  * This is a common method which handles both
142  * ipz and keyword specific interfaces thus,
143  * reducing the code redundancy.
144  * @param[in] map - Reference to the innermost keyword-value map.
145  * @param[in] preIntrStr - Reference to the interface string.
146  * @param[out] interfaces - Reference to interface map.
147  */
148 template <typename T>
149 static void populateFruSpecificInterfaces(const T& map,
150                                           const string& preIntrStr,
151                                           inventory::InterfaceMap& interfaces)
152 {
153     inventory::PropertyMap prop;
154 
155     for (const auto& kwVal : map)
156     {
157         vector<uint8_t> vec(kwVal.second.begin(), kwVal.second.end());
158 
159         auto kw = kwVal.first;
160 
161         if (kw[0] == '#')
162         {
163             kw = string("PD_") + kw[1];
164         }
165         else if (isdigit(kw[0]))
166         {
167             kw = string("N_") + kw;
168         }
169         prop.emplace(move(kw), move(vec));
170     }
171 
172     interfaces.emplace(preIntrStr, move(prop));
173 }
174 
175 /**
176  * @brief Populate Interfaces.
177  *
178  * This method populates common and extra interfaces to dbus.
179  * @param[in] js - json object
180  * @param[out] interfaces - Reference to interface map
181  * @param[in] vpdMap - Reference to the parsed vpd map.
182  * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
183  */
184 template <typename T>
185 static void populateInterfaces(const nlohmann::json& js,
186                                inventory::InterfaceMap& interfaces,
187                                const T& vpdMap, bool isSystemVpd)
188 {
189     for (const auto& ifs : js.items())
190     {
191         string inf = ifs.key();
192         inventory::PropertyMap props;
193 
194         for (const auto& itr : ifs.value().items())
195         {
196             const string& busProp = itr.key();
197 
198             if (itr.value().is_boolean())
199             {
200                 props.emplace(busProp, itr.value().get<bool>());
201             }
202             else if (itr.value().is_string())
203             {
204                 if constexpr (is_same<T, Parsed>::value)
205                 {
206                     if (busProp == "LocationCode" &&
207                         inf == "com.ibm.ipzvpd.Location")
208                     {
209                         auto prop = expandLocationCode(
210                             itr.value().get<string>(), vpdMap, isSystemVpd);
211                         props.emplace(busProp, prop);
212                     }
213                     else
214                     {
215                         props.emplace(busProp, itr.value().get<string>());
216                     }
217                 }
218                 else
219                 {
220                     props.emplace(busProp, itr.value().get<string>());
221                 }
222             }
223             else if (itr.value().is_object())
224             {
225                 const string& rec = itr.value().value("recordName", "");
226                 const string& kw = itr.value().value("keywordName", "");
227                 const string& encoding = itr.value().value("encoding", "");
228 
229                 if constexpr (is_same<T, Parsed>::value)
230                 {
231                     if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
232                         vpdMap.at(rec).count(kw))
233                     {
234                         auto encoded =
235                             encodeKeyword(vpdMap.at(rec).at(kw), encoding);
236                         props.emplace(busProp, encoded);
237                     }
238                 }
239                 else if constexpr (is_same<T, KeywordVpdMap>::value)
240                 {
241                     if (!kw.empty() && vpdMap.count(kw))
242                     {
243                         auto prop =
244                             string(vpdMap.at(kw).begin(), vpdMap.at(kw).end());
245                         auto encoded = encodeKeyword(prop, encoding);
246                         props.emplace(busProp, encoded);
247                     }
248                 }
249             }
250         }
251         interfaces.emplace(inf, move(props));
252     }
253 }
254 
255 static Binary getVpdDataInVector(const nlohmann::json& js, const string& file)
256 {
257     uint32_t offset = 0;
258     // check if offset present?
259     for (const auto& item : js["frus"][file])
260     {
261         if (item.find("offset") != item.end())
262         {
263             offset = item["offset"];
264         }
265     }
266 
267     // TODO: Figure out a better way to get max possible VPD size.
268     Binary vpdVector;
269     vpdVector.resize(65504);
270     ifstream vpdFile;
271     vpdFile.open(file, ios::binary);
272 
273     vpdFile.seekg(offset, ios_base::cur);
274     vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), 65504);
275     vpdVector.resize(vpdFile.gcount());
276 
277     return vpdVector;
278 }
279 
280 /* It does nothing. Just an empty function to return null
281  * at the end of variadic template args
282  */
283 static string getCommand()
284 {
285     return "";
286 }
287 
288 /* This function to arrange all arguments to make command
289  */
290 template <typename T, typename... Types>
291 static string getCommand(T arg1, Types... args)
292 {
293     string cmd = " " + arg1 + getCommand(args...);
294 
295     return cmd;
296 }
297 
298 /* This API takes arguments and run that command
299  * returns output of that command
300  */
301 template <typename T, typename... Types>
302 static vector<string> executeCmd(T&& path, Types... args)
303 {
304     vector<string> stdOutput;
305     array<char, 128> buffer;
306 
307     string cmd = path + getCommand(args...);
308 
309     unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
310     if (!pipe)
311     {
312         throw runtime_error("popen() failed!");
313     }
314     while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
315     {
316         stdOutput.emplace_back(buffer.data());
317     }
318 
319     return stdOutput;
320 }
321 
322 /** This API will be called at the end of VPD collection to perform any post
323  * actions.
324  *
325  * @param[in] json - json object
326  * @param[in] file - eeprom file path
327  */
328 static void postFailAction(const nlohmann::json& json, const string& file)
329 {
330     if ((json["frus"][file].at(0)).find("postActionFail") ==
331         json["frus"][file].at(0).end())
332     {
333         return;
334     }
335 
336     uint8_t pinValue = 0;
337     string pinName;
338 
339     for (const auto& postAction :
340          (json["frus"][file].at(0))["postActionFail"].items())
341     {
342         if (postAction.key() == "pin")
343         {
344             pinName = postAction.value();
345         }
346         else if (postAction.key() == "value")
347         {
348             // Get the value to set
349             pinValue = postAction.value();
350         }
351     }
352 
353     cout << "Setting GPIO: " << pinName << " to " << (int)pinValue << endl;
354 
355     try
356     {
357         gpiod::line outputLine = gpiod::find_line(pinName);
358 
359         if (!outputLine)
360         {
361             cout << "Couldn't find output line:" << pinName
362                  << " on GPIO. Skipping...\n";
363 
364             return;
365         }
366         outputLine.request(
367             {"Disable line", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
368             pinValue);
369     }
370     catch (system_error&)
371     {
372         cerr << "Failed to set post-action GPIO" << endl;
373     }
374 }
375 
376 /** Performs any pre-action needed to get the FRU setup for collection.
377  *
378  * @param[in] json - json object
379  * @param[in] file - eeprom file path
380  */
381 static void preAction(const nlohmann::json& json, const string& file)
382 {
383     if ((json["frus"][file].at(0)).find("preAction") ==
384         json["frus"][file].at(0).end())
385     {
386         return;
387     }
388 
389     uint8_t pinValue = 0;
390     string pinName;
391 
392     for (const auto& postAction :
393          (json["frus"][file].at(0))["preAction"].items())
394     {
395         if (postAction.key() == "pin")
396         {
397             pinName = postAction.value();
398         }
399         else if (postAction.key() == "value")
400         {
401             // Get the value to set
402             pinValue = postAction.value();
403         }
404     }
405 
406     cout << "Setting GPIO: " << pinName << " to " << (int)pinValue << endl;
407     try
408     {
409         gpiod::line outputLine = gpiod::find_line(pinName);
410 
411         if (!outputLine)
412         {
413             cout << "Couldn't find output line:" << pinName
414                  << " on GPIO. Skipping...\n";
415 
416             return;
417         }
418         outputLine.request(
419             {"FRU pre-action", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
420             pinValue);
421     }
422     catch (system_error&)
423     {
424         cerr << "Failed to set pre-action GPIO" << endl;
425         return;
426     }
427 
428     // Now bind the device
429     string bind = json["frus"][file].at(0).value("bind", "");
430     cout << "Binding device " << bind << endl;
431     string bindCmd = string("echo \"") + bind +
432                      string("\" > /sys/bus/i2c/drivers/at24/bind");
433     cout << bindCmd << endl;
434     executeCmd(bindCmd);
435 
436     // Check if device showed up (test for file)
437     if (!fs::exists(file))
438     {
439         cout << "EEPROM " << file << " does not exist. Take failure action"
440              << endl;
441         // If not, then take failure postAction
442         postFailAction(json, file);
443     }
444 }
445 
446 /**
447  * @brief Prime the Inventory
448  * Prime the inventory by populating only the location code,
449  * type interface and the inventory object for the frus
450  * which are not system vpd fru.
451  *
452  * @param[in] jsObject - Reference to vpd inventory json object
453  * @param[in] vpdMap -  Reference to the parsed vpd map
454  *
455  * @returns Map of items in extraInterface.
456  */
457 template <typename T>
458 inventory::ObjectMap primeInventory(const nlohmann::json& jsObject,
459                                     const T& vpdMap)
460 {
461     inventory::ObjectMap objects;
462 
463     for (auto& itemFRUS : jsObject["frus"].items())
464     {
465         // Take pre actions
466         preAction(jsObject, itemFRUS.key());
467         for (auto& itemEEPROM : itemFRUS.value())
468         {
469             inventory::InterfaceMap interfaces;
470             auto isSystemVpd = itemEEPROM.value("isSystemVpd", false);
471             inventory::Object object(itemEEPROM.at("inventoryPath"));
472 
473             if (!isSystemVpd && !itemEEPROM.value("noprime", false))
474             {
475                 if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
476                 {
477                     for (const auto& eI : itemEEPROM["extraInterfaces"].items())
478                     {
479                         inventory::PropertyMap props;
480                         if (eI.key() ==
481                             openpower::vpd::constants::LOCATION_CODE_INF)
482                         {
483                             if constexpr (std::is_same<T, Parsed>::value)
484                             {
485                                 for (auto& lC : eI.value().items())
486                                 {
487                                     auto propVal = expandLocationCode(
488                                         lC.value().get<string>(), vpdMap, true);
489 
490                                     props.emplace(move(lC.key()),
491                                                   move(propVal));
492                                     interfaces.emplace(move(eI.key()),
493                                                        move(props));
494                                 }
495                             }
496                         }
497                         else if (eI.key().find("Inventory.Item.") !=
498                                  string::npos)
499                         {
500                             interfaces.emplace(move(eI.key()), move(props));
501                         }
502                     }
503                 }
504                 objects.emplace(move(object), move(interfaces));
505             }
506         }
507     }
508     return objects;
509 }
510 
511 /**
512  * @brief This API executes command to set environment variable
513  *        And then reboot the system
514  * @param[in] key   -env key to set new value
515  * @param[in] value -value to set.
516  */
517 void setEnvAndReboot(const string& key, const string& value)
518 {
519     // set env and reboot and break.
520     executeCmd("/sbin/fw_setenv", key, value);
521     log<level::INFO>("Rebooting BMC to pick up new device tree");
522     // make dbus call to reboot
523     auto bus = sdbusplus::bus::new_default_system();
524     auto method = bus.new_method_call(
525         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
526         "org.freedesktop.systemd1.Manager", "Reboot");
527     bus.call_noreply(method);
528 }
529 
530 /*
531  * @brief This API checks for env var fitconfig.
532  *        If not initialised OR updated as per the current system type,
533  *        update this env var and reboot the system.
534  *
535  * @param[in] systemType IM kwd in vpd tells about which system type it is.
536  * */
537 void setDevTreeEnv(const string& systemType)
538 {
539     string newDeviceTree;
540 
541     if (deviceTreeSystemTypeMap.find(systemType) !=
542         deviceTreeSystemTypeMap.end())
543     {
544         newDeviceTree = deviceTreeSystemTypeMap.at(systemType);
545     }
546 
547     string readVarValue;
548     bool envVarFound = false;
549 
550     vector<string> output = executeCmd("/sbin/fw_printenv");
551     for (const auto& entry : output)
552     {
553         size_t pos = entry.find("=");
554         string key = entry.substr(0, pos);
555         if (key != "fitconfig")
556         {
557             continue;
558         }
559 
560         envVarFound = true;
561         if (pos + 1 < entry.size())
562         {
563             readVarValue = entry.substr(pos + 1);
564             if (readVarValue.find(newDeviceTree) != string::npos)
565             {
566                 // fitconfig is Updated. No action needed
567                 break;
568             }
569         }
570         // set env and reboot and break.
571         setEnvAndReboot(key, newDeviceTree);
572         exit(0);
573     }
574 
575     // check If env var Not found
576     if (!envVarFound)
577     {
578         setEnvAndReboot("fitconfig", newDeviceTree);
579     }
580 }
581 
582 /**
583  * @brief Populate Dbus.
584  * This method invokes all the populateInterface functions
585  * and notifies PIM about dbus object.
586  * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
587  * input.
588  * @param[in] js - Inventory json object
589  * @param[in] filePath - Path of the vpd file
590  * @param[in] preIntrStr - Interface string
591  */
592 template <typename T>
593 static void populateDbus(const T& vpdMap, nlohmann::json& js,
594                          const string& filePath)
595 {
596     inventory::InterfaceMap interfaces;
597     inventory::ObjectMap objects;
598     inventory::PropertyMap prop;
599 
600     bool isSystemVpd = false;
601     for (const auto& item : js["frus"][filePath])
602     {
603         const auto& objectPath = item["inventoryPath"];
604         sdbusplus::message::object_path object(objectPath);
605         isSystemVpd = item.value("isSystemVpd", false);
606         // Populate the VPD keywords and the common interfaces only if we
607         // are asked to inherit that data from the VPD, else only add the
608         // extraInterfaces.
609         if (item.value("inherit", true))
610         {
611             if constexpr (is_same<T, Parsed>::value)
612             {
613                 // Each record in the VPD becomes an interface and all
614                 // keyword within the record are properties under that
615                 // interface.
616                 for (const auto& record : vpdMap)
617                 {
618                     populateFruSpecificInterfaces(
619                         record.second, ipzVpdInf + record.first, interfaces);
620                 }
621             }
622             else if constexpr (is_same<T, KeywordVpdMap>::value)
623             {
624                 populateFruSpecificInterfaces(vpdMap, kwdVpdInf, interfaces);
625             }
626             if (js.find("commonInterfaces") != js.end())
627             {
628                 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
629                                    isSystemVpd);
630             }
631         }
632         else
633         {
634             // Check if we have been asked to inherit specific record(s)
635             if constexpr (is_same<T, Parsed>::value)
636             {
637                 if (item.find("copyRecords") != item.end())
638                 {
639                     for (const auto& record : item["copyRecords"])
640                     {
641                         const string& recordName = record;
642                         if (vpdMap.find(recordName) != vpdMap.end())
643                         {
644                             populateFruSpecificInterfaces(
645                                 vpdMap.at(recordName), ipzVpdInf + recordName,
646                                 interfaces);
647                         }
648                     }
649                 }
650             }
651         }
652 
653         if (item.value("inheritEI", true))
654         {
655             // Populate interfaces and properties that are common to every FRU
656             // and additional interface that might be defined on a per-FRU
657             // basis.
658             if (item.find("extraInterfaces") != item.end())
659             {
660                 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
661                                    isSystemVpd);
662             }
663         }
664         objects.emplace(move(object), move(interfaces));
665     }
666 
667     if (isSystemVpd)
668     {
669         vector<uint8_t> imVal;
670         if constexpr (is_same<T, Parsed>::value)
671         {
672             auto property = vpdMap.find("VSBP");
673             if (property != vpdMap.end())
674             {
675                 auto value = (property->second).find("IM");
676                 if (value != (property->second).end())
677                 {
678                     copy(value->second.begin(), value->second.end(),
679                          back_inserter(imVal));
680                 }
681             }
682         }
683 
684         fs::path target;
685         fs::path link = INVENTORY_JSON_SYM_LINK;
686 
687         ostringstream oss;
688         for (auto& i : imVal)
689         {
690             oss << setw(2) << setfill('0') << hex << static_cast<int>(i);
691         }
692         string imValStr = oss.str();
693 
694         if (imValStr == RAINIER_4U) // 4U
695         {
696             target = INVENTORY_JSON_4U;
697         }
698         else if (imValStr == RAINIER_2U) // 2U
699         {
700             target = INVENTORY_JSON_2U;
701         }
702         else if (imValStr == EVEREST)
703         {
704             target = INVENTORY_JSON_EVEREST;
705         }
706 
707         // Create the directory for hosting the symlink
708         fs::create_directories(VPD_FILES_PATH);
709         // unlink the symlink previously created (if any)
710         remove(INVENTORY_JSON_SYM_LINK);
711         // create a new symlink based on the system
712         fs::create_symlink(target, link);
713 
714         // Reloading the json
715         ifstream inventoryJson(link);
716         auto js = json::parse(inventoryJson);
717         inventoryJson.close();
718 
719         inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
720         objects.insert(primeObject.begin(), primeObject.end());
721 
722         // set the U-boot environment variable for device-tree
723         setDevTreeEnv(imValStr);
724     }
725 
726     // Notify PIM
727     inventory::callPIM(move(objects));
728 }
729 
730 int main(int argc, char** argv)
731 {
732     int rc = 0;
733     string file{};
734     json js{};
735 
736     // map to hold additional data in case of logging pel
737     PelAdditionalData additionalData{};
738 
739     // this is needed to hold base fru inventory path in case there is ECC or
740     // vpd exception while parsing the file
741     std::string baseFruInventoryPath = {};
742 
743     try
744     {
745         App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
746                 "in DBUS"};
747         string file{};
748 
749         app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
750             ->required();
751 
752         CLI11_PARSE(app, argc, argv);
753 
754         auto jsonToParse = INVENTORY_JSON_DEFAULT;
755 
756         // If the symlink exists, it means it has been setup for us, switch the
757         // path
758         if (fs::exists(INVENTORY_JSON_SYM_LINK))
759         {
760             jsonToParse = INVENTORY_JSON_SYM_LINK;
761         }
762 
763         // Make sure that the file path we get is for a supported EEPROM
764         ifstream inventoryJson(jsonToParse);
765         if (!inventoryJson)
766         {
767             throw(
768                 (VpdJsonException("Failed to access Json path", jsonToParse)));
769         }
770 
771         try
772         {
773             js = json::parse(inventoryJson);
774         }
775         catch (json::parse_error& ex)
776         {
777             throw((VpdJsonException("Json parsing failed", jsonToParse)));
778         }
779 
780         if ((js.find("frus") == js.end()) ||
781             (js["frus"].find(file) == js["frus"].end()))
782         {
783             cout << "Device path not in JSON, ignoring" << endl;
784             return 0;
785         }
786 
787         if (!fs::exists(file))
788         {
789             cout << "Device path: " << file
790                  << " does not exist. Spurious udev event? Exiting." << endl;
791             return 0;
792         }
793 
794         baseFruInventoryPath = js["frus"][file][0]["inventoryPath"];
795         // Check if we can read the VPD file based on the power state
796         if (js["frus"][file].at(0).value("powerOffOnly", false))
797         {
798             if ("xyz.openbmc_project.State.Chassis.PowerState.On" ==
799                 getPowerState())
800             {
801                 cout << "This VPD cannot be read when power is ON" << endl;
802                 return 0;
803             }
804         }
805 
806         Binary vpdVector = getVpdDataInVector(js, file);
807         ParserInterface* parser = ParserFactory::getParser(move(vpdVector));
808 
809         variant<KeywordVpdMap, Store> parseResult;
810         parseResult = parser->parse();
811 
812         try
813         {
814             Binary vpdVector = getVpdDataInVector(js, file);
815             ParserInterface* parser = ParserFactory::getParser(move(vpdVector));
816 
817             variant<KeywordVpdMap, Store> parseResult;
818             parseResult = parser->parse();
819             if (auto pVal = get_if<Store>(&parseResult))
820             {
821                 populateDbus(pVal->getVpdMap(), js, file);
822             }
823             else if (auto pVal = get_if<KeywordVpdMap>(&parseResult))
824             {
825                 populateDbus(*pVal, js, file);
826             }
827 
828             // release the parser object
829             ParserFactory::freeParser(parser);
830         }
831         catch (exception& e)
832         {
833             postFailAction(js, file);
834             throw e;
835         }
836     }
837     catch (const VpdJsonException& ex)
838     {
839         additionalData.emplace("JSON_PATH", ex.getJsonPath());
840         additionalData.emplace("DESCRIPTION", ex.what());
841         createPEL(additionalData, errIntfForJsonFailure);
842 
843         cerr << ex.what() << "\n";
844         rc = -1;
845     }
846     catch (const VpdEccException& ex)
847     {
848         additionalData.emplace("DESCRIPTION", "ECC check failed");
849         additionalData.emplace("CALLOUT_INVENTORY_PATH",
850                                INVENTORY_PATH + baseFruInventoryPath);
851         createPEL(additionalData, errIntfForEccCheckFail);
852 
853         cerr << ex.what() << "\n";
854         rc = -1;
855     }
856     catch (const VpdDataException& ex)
857     {
858         additionalData.emplace("DESCRIPTION", "Invalid VPD data");
859         additionalData.emplace("CALLOUT_INVENTORY_PATH",
860                                INVENTORY_PATH + baseFruInventoryPath);
861         createPEL(additionalData, errIntfForInvalidVPD);
862 
863         cerr << ex.what() << "\n";
864         rc = -1;
865     }
866     catch (exception& e)
867     {
868         cerr << e.what() << "\n";
869         rc = -1;
870     }
871 
872     return rc;
873 }
874