1 #include "config.h"
2 
3 #include "common_utility.hpp"
4 #include "defines.hpp"
5 #include "editor_impl.hpp"
6 #include "ibm_vpd_utils.hpp"
7 #include "ipz_parser.hpp"
8 #include "keyword_vpd_parser.hpp"
9 #include "memory_vpd_parser.hpp"
10 #include "parser_factory.hpp"
11 #include "vpd_exceptions.hpp"
12 
13 #include <assert.h>
14 #include <ctype.h>
15 
16 #include <CLI/CLI.hpp>
17 #include <boost/algorithm/string.hpp>
18 #include <gpiod.hpp>
19 #include <phosphor-logging/log.hpp>
20 
21 #include <algorithm>
22 #include <cstdarg>
23 #include <exception>
24 #include <filesystem>
25 #include <fstream>
26 #include <iostream>
27 #include <iterator>
28 #include <regex>
29 #include <thread>
30 
31 using namespace std;
32 using namespace openpower::vpd;
33 using namespace CLI;
34 using namespace vpd::keyword::parser;
35 using namespace openpower::vpd::constants;
36 namespace fs = filesystem;
37 using json = nlohmann::json;
38 using namespace openpower::vpd::parser::factory;
39 using namespace openpower::vpd::inventory;
40 using namespace openpower::vpd::memory::parser;
41 using namespace openpower::vpd::parser::interface;
42 using namespace openpower::vpd::exceptions;
43 using namespace phosphor::logging;
44 using namespace openpower::vpd::manager::editor;
45 
46 /**
47  * @brief API declaration, Populate Dbus.
48  *
49  * This method invokes all the populateInterface functions
50  * and notifies PIM about dbus object.
51  *
52  * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
53  * input.
54  * @param[in] js - Inventory json object
55  * @param[in] filePath - Path of the vpd file
56  * @param[in] preIntrStr - Interface string
57  */
58 template <typename T>
59 static void populateDbus(T& vpdMap, nlohmann::json& js, const string& filePath);
60 
61 /**
62  * @brief Returns the BMC state
63  */
64 static auto getBMCState()
65 {
66     std::string bmcState;
67     try
68     {
69         auto bus = sdbusplus::bus::new_default();
70         auto properties = bus.new_method_call(
71             "xyz.openbmc_project.State.BMC", "/xyz/openbmc_project/state/bmc0",
72             "org.freedesktop.DBus.Properties", "Get");
73         properties.append("xyz.openbmc_project.State.BMC");
74         properties.append("CurrentBMCState");
75         auto result = bus.call(properties);
76         std::variant<std::string> val;
77         result.read(val);
78         if (auto pVal = std::get_if<std::string>(&val))
79         {
80             bmcState = *pVal;
81         }
82     }
83     catch (const sdbusplus::exception::SdBusError& e)
84     {
85         // Ignore any error
86         std::cerr << "Failed to get BMC state: " << e.what() << "\n";
87     }
88     return bmcState;
89 }
90 
91 /**
92  * @brief Check if the FRU is in the cache
93  *
94  * Checks if the FRU associated with the supplied D-Bus object path is already
95  * on D-Bus. This can be used to test if a VPD collection is required for this
96  * FRU. It uses the "xyz.openbmc_project.Inventory.Item, Present" property to
97  * determine the presence of a FRU in the cache.
98  *
99  * @param objectPath - The D-Bus object path without the PIM prefix.
100  * @return true if the object exists on D-Bus, false otherwise.
101  */
102 static auto isFruInVpdCache(const std::string& objectPath)
103 {
104     try
105     {
106         auto bus = sdbusplus::bus::new_default();
107         auto invPath = std::string{pimPath} + objectPath;
108         auto props = bus.new_method_call(
109             "xyz.openbmc_project.Inventory.Manager", invPath.c_str(),
110             "org.freedesktop.DBus.Properties", "Get");
111         props.append("xyz.openbmc_project.Inventory.Item");
112         props.append("Present");
113         auto result = bus.call(props);
114         std::variant<bool> present;
115         result.read(present);
116         if (auto pVal = std::get_if<bool>(&present))
117         {
118             return *pVal;
119         }
120         return false;
121     }
122     catch (const sdbusplus::exception::SdBusError& e)
123     {
124         std::cout << "FRU: " << objectPath << " not in D-Bus\n";
125         // Assume not present in case of an error
126         return false;
127     }
128 }
129 
130 /**
131  * @brief Check if VPD recollection is needed for the given EEPROM
132  *
133  * Not all FRUs can be swapped at BMC ready state. This function does the
134  * following:
135  * -- Check if the FRU is marked as "pluggableAtStandby" OR
136  *    "concurrentlyMaintainable". If so, return true.
137  * -- Check if we are at BMC NotReady state. If we are, then return true.
138  * -- Else check if the FRU is not present in the VPD cache (to cover for VPD
139  *    force collection). If not found in the cache, return true.
140  * -- Else return false.
141  *
142  * @param js - JSON Object.
143  * @param filePath - The EEPROM file.
144  * @return true if collection should be attempted, false otherwise.
145  */
146 static auto needsRecollection(const nlohmann::json& js, const string& filePath)
147 {
148     if (js["frus"][filePath].at(0).value("pluggableAtStandby", false) ||
149         js["frus"][filePath].at(0).value("concurrentlyMaintainable", false))
150     {
151         return true;
152     }
153     if (getBMCState() == "xyz.openbmc_project.State.BMC.BMCState.NotReady")
154     {
155         return true;
156     }
157     if (!isFruInVpdCache(js["frus"][filePath].at(0).value("inventoryPath", "")))
158     {
159         return true;
160     }
161     return false;
162 }
163 
164 /**
165  * @brief Expands location codes
166  */
167 static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
168                                bool isSystemVpd)
169 {
170     auto expanded{unexpanded};
171     static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
172     static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
173     static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
174     size_t idx = expanded.find("fcs");
175     try
176     {
177         if (idx != string::npos)
178         {
179             string fc{};
180             string se{};
181             if (isSystemVpd)
182             {
183                 const auto& fcData = vpdMap.at("VCEN").at("FC");
184                 const auto& seData = vpdMap.at("VCEN").at("SE");
185                 fc = string(fcData.data(), fcData.size());
186                 se = string(seData.data(), seData.size());
187             }
188             else
189             {
190                 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
191                 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
192             }
193 
194             // TODO: See if ND0 can be placed in the JSON
195             expanded.replace(idx, 3, fc.substr(0, 4) + ".ND0." + se);
196         }
197         else
198         {
199             idx = expanded.find("mts");
200             if (idx != string::npos)
201             {
202                 string mt{};
203                 string se{};
204                 if (isSystemVpd)
205                 {
206                     const auto& mtData = vpdMap.at("VSYS").at("TM");
207                     const auto& seData = vpdMap.at("VSYS").at("SE");
208                     mt = string(mtData.data(), mtData.size());
209                     se = string(seData.data(), seData.size());
210                 }
211                 else
212                 {
213                     mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
214                     se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
215                 }
216 
217                 replace(mt.begin(), mt.end(), '-', '.');
218                 expanded.replace(idx, 3, mt + "." + se);
219             }
220         }
221     }
222     catch (const exception& e)
223     {
224         std::cerr << "Failed to expand location code with exception: "
225                   << e.what() << "\n";
226     }
227     return expanded;
228 }
229 
230 /**
231  * @brief Populate FRU specific interfaces.
232  *
233  * This is a common method which handles both
234  * ipz and keyword specific interfaces thus,
235  * reducing the code redundancy.
236  * @param[in] map - Reference to the innermost keyword-value map.
237  * @param[in] preIntrStr - Reference to the interface string.
238  * @param[out] interfaces - Reference to interface map.
239  */
240 template <typename T>
241 static void populateFruSpecificInterfaces(const T& map,
242                                           const string& preIntrStr,
243                                           inventory::InterfaceMap& interfaces)
244 {
245     inventory::PropertyMap prop;
246 
247     for (const auto& kwVal : map)
248     {
249         auto kw = kwVal.first;
250 
251         if (kw[0] == '#')
252         {
253             kw = string("PD_") + kw[1];
254         }
255         else if (isdigit(kw[0]))
256         {
257             kw = string("N_") + kw;
258         }
259         if constexpr (is_same<T, KeywordVpdMap>::value)
260         {
261             if (auto keywordValue = get_if<Binary>(&kwVal.second))
262             {
263                 Binary vec((*keywordValue).begin(), (*keywordValue).end());
264                 prop.emplace(move(kw), move(vec));
265             }
266             else if (auto keywordValue = get_if<std::string>(&kwVal.second))
267             {
268                 Binary vec((*keywordValue).begin(), (*keywordValue).end());
269                 prop.emplace(move(kw), move(vec));
270             }
271             else if (auto keywordValue = get_if<size_t>(&kwVal.second))
272             {
273                 if (kw == "MemorySizeInKB")
274                 {
275                     inventory::PropertyMap memProp;
276                     memProp.emplace(move(kw), ((*keywordValue)));
277                     interfaces.emplace(
278                         "xyz.openbmc_project.Inventory.Item.Dimm",
279                         move(memProp));
280                 }
281                 else
282                 {
283                     std::cerr << "Unknown Keyword[" << kw << "] found ";
284                 }
285             }
286             else
287             {
288                 std::cerr << "Unknown Variant found ";
289             }
290         }
291         else
292         {
293             Binary vec(kwVal.second.begin(), kwVal.second.end());
294             prop.emplace(move(kw), move(vec));
295         }
296     }
297 
298     interfaces.emplace(preIntrStr, move(prop));
299 }
300 
301 /**
302  * @brief Populate Interfaces.
303  *
304  * This method populates common and extra interfaces to dbus.
305  * @param[in] js - json object
306  * @param[out] interfaces - Reference to interface map
307  * @param[in] vpdMap - Reference to the parsed vpd map.
308  * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
309  */
310 template <typename T>
311 static void populateInterfaces(const nlohmann::json& js,
312                                inventory::InterfaceMap& interfaces,
313                                const T& vpdMap, bool isSystemVpd)
314 {
315     for (const auto& ifs : js.items())
316     {
317         string inf = ifs.key();
318         inventory::PropertyMap props;
319 
320         for (const auto& itr : ifs.value().items())
321         {
322             const string& busProp = itr.key();
323 
324             if (itr.value().is_boolean())
325             {
326                 props.emplace(busProp, itr.value().get<bool>());
327             }
328             else if (itr.value().is_string())
329             {
330                 if (busProp == "LocationCode" && inf == IBM_LOCATION_CODE_INF)
331                 {
332                     std::string prop;
333                     if constexpr (is_same<T, Parsed>::value)
334                     {
335                         // TODO deprecate the com.ibm interface later
336                         prop = expandLocationCode(itr.value().get<string>(),
337                                                   vpdMap, isSystemVpd);
338                     }
339                     else if constexpr (is_same<T, KeywordVpdMap>::value)
340                     {
341                         // Send empty Parsed object to expandLocationCode api.
342                         prop = expandLocationCode(itr.value().get<string>(),
343                                                   Parsed{}, false);
344                     }
345                     props.emplace(busProp, prop);
346                     interfaces.emplace(XYZ_LOCATION_CODE_INF, props);
347                     interfaces.emplace(IBM_LOCATION_CODE_INF, props);
348                 }
349                 else
350                 {
351                     props.emplace(busProp, itr.value().get<string>());
352                 }
353             }
354             else if (itr.value().is_array())
355             {
356                 try
357                 {
358                     props.emplace(busProp, itr.value().get<Binary>());
359                 }
360                 catch (const nlohmann::detail::type_error& e)
361                 {
362                     std::cerr << "Type exception: " << e.what() << "\n";
363                     // Ignore any type errors
364                 }
365             }
366             else if (itr.value().is_object())
367             {
368                 const string& rec = itr.value().value("recordName", "");
369                 const string& kw = itr.value().value("keywordName", "");
370                 const string& encoding = itr.value().value("encoding", "");
371 
372                 if constexpr (is_same<T, Parsed>::value)
373                 {
374                     if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
375                         vpdMap.at(rec).count(kw))
376                     {
377                         auto encoded = encodeKeyword(vpdMap.at(rec).at(kw),
378                                                      encoding);
379                         props.emplace(busProp, encoded);
380                     }
381                 }
382                 else if constexpr (is_same<T, KeywordVpdMap>::value)
383                 {
384                     if (!kw.empty() && vpdMap.count(kw))
385                     {
386                         if (auto kwValue = get_if<Binary>(&vpdMap.at(kw)))
387                         {
388                             auto prop = string((*kwValue).begin(),
389                                                (*kwValue).end());
390 
391                             auto encoded = encodeKeyword(prop, encoding);
392 
393                             props.emplace(busProp, encoded);
394                         }
395                         else if (auto kwValue =
396                                      get_if<std::string>(&vpdMap.at(kw)))
397                         {
398                             auto prop = string((*kwValue).begin(),
399                                                (*kwValue).end());
400 
401                             auto encoded = encodeKeyword(prop, encoding);
402 
403                             props.emplace(busProp, encoded);
404                         }
405                         else if (auto uintValue =
406                                      get_if<size_t>(&vpdMap.at(kw)))
407                         {
408                             props.emplace(busProp, *uintValue);
409                         }
410                         else
411                         {
412                             std::cerr << " Unknown Keyword [" << kw
413                                       << "] Encountered";
414                         }
415                     }
416                 }
417             }
418             else if (itr.value().is_number())
419             {
420                 // For now assume the value is a size_t.  In the future it would
421                 // be nice to come up with a way to get the type from the JSON.
422                 props.emplace(busProp, itr.value().get<size_t>());
423             }
424         }
425         insertOrMerge(interfaces, inf, move(props));
426     }
427 }
428 
429 /**
430  * @brief This API checks if this FRU is pcie_devices. If yes then it further
431  *        checks whether it is PASS1 planar.
432  */
433 static bool isThisPcieOnPass1planar(const nlohmann::json& js,
434                                     const string& file)
435 {
436     auto isThisPCIeDev = false;
437     auto isPASS1 = false;
438 
439     // Check if it is a PCIE device
440     if (js["frus"].find(file) != js["frus"].end())
441     {
442         if ((js["frus"][file].at(0).find("extraInterfaces") !=
443              js["frus"][file].at(0).end()))
444         {
445             if (js["frus"][file].at(0)["extraInterfaces"].find(
446                     "xyz.openbmc_project.Inventory.Item.PCIeDevice") !=
447                 js["frus"][file].at(0)["extraInterfaces"].end())
448             {
449                 isThisPCIeDev = true;
450             }
451         }
452     }
453 
454     if (isThisPCIeDev)
455     {
456         // Collect HW version and SystemType to know if it is PASS1 planar.
457         auto bus = sdbusplus::bus::new_default();
458         auto property1 = bus.new_method_call(
459             INVENTORY_MANAGER_SERVICE,
460             "/xyz/openbmc_project/inventory/system/chassis/motherboard",
461             "org.freedesktop.DBus.Properties", "Get");
462         property1.append("com.ibm.ipzvpd.VINI");
463         property1.append("HW");
464         auto result1 = bus.call(property1);
465         inventory::Value hwVal;
466         result1.read(hwVal);
467 
468         // SystemType
469         auto property2 = bus.new_method_call(
470             INVENTORY_MANAGER_SERVICE,
471             "/xyz/openbmc_project/inventory/system/chassis/motherboard",
472             "org.freedesktop.DBus.Properties", "Get");
473         property2.append("com.ibm.ipzvpd.VSBP");
474         property2.append("IM");
475         auto result2 = bus.call(property2);
476         inventory::Value imVal;
477         result2.read(imVal);
478 
479         auto pVal1 = get_if<Binary>(&hwVal);
480         auto pVal2 = get_if<Binary>(&imVal);
481 
482         if (pVal1 && pVal2)
483         {
484             auto hwVersion = *pVal1;
485             auto systemType = *pVal2;
486 
487             // IM kw for Everest
488             Binary everestSystem{80, 00, 48, 00};
489 
490             if (systemType == everestSystem)
491             {
492                 if (hwVersion[1] < 21)
493                 {
494                     isPASS1 = true;
495                 }
496             }
497             else if (hwVersion[1] < 2)
498             {
499                 isPASS1 = true;
500             }
501         }
502     }
503 
504     return (isThisPCIeDev && isPASS1);
505 }
506 
507 /** Performs any pre-action needed to get the FRU setup for collection.
508  *
509  * @param[in] json - json object
510  * @param[in] file - eeprom file path
511  */
512 static void preAction(const nlohmann::json& json, const string& file)
513 {
514     if ((json["frus"][file].at(0)).find("preAction") ==
515         json["frus"][file].at(0).end())
516     {
517         return;
518     }
519 
520     try
521     {
522         if (executePreAction(json, file))
523         {
524             if (json["frus"][file].at(0).find("devAddress") !=
525                 json["frus"][file].at(0).end())
526             {
527                 // Now bind the device
528                 string bind = json["frus"][file].at(0).value("devAddress", "");
529                 std::cout << "Binding device " << bind << std::endl;
530                 string bindCmd = string("echo \"") + bind +
531                                  string("\" > /sys/bus/i2c/drivers/at24/bind");
532                 std::cout << bindCmd << std::endl;
533                 executeCmd(bindCmd);
534 
535                 // Check if device showed up (test for file)
536                 if (!fs::exists(file))
537                 {
538                     std::cerr << "EEPROM " << file
539                               << " does not exist. Take failure action"
540                               << std::endl;
541                     // If not, then take failure postAction
542                     executePostFailAction(json, file);
543                 }
544             }
545             else
546             {
547                 // missing required informations
548                 std::cerr << "VPD inventory JSON missing basic informations of "
549                              "preAction "
550                              "for this FRU : ["
551                           << file << "]. Executing executePostFailAction."
552                           << std::endl;
553 
554                 // Take failure postAction
555                 executePostFailAction(json, file);
556                 return;
557             }
558         }
559         else
560         {
561             // If the FRU is not there, clear the VINI/CCIN data.
562             // Enity manager probes for this keyword to look for this
563             // FRU, now if the data is persistent on BMC and FRU is
564             // removed this can lead to ambiguity. Hence clearing this
565             // Keyword if FRU is absent.
566             const auto& invPath =
567                 json["frus"][file].at(0).value("inventoryPath", "");
568 
569             if (!invPath.empty())
570             {
571                 inventory::ObjectMap pimObjMap{
572                     {invPath, {{"com.ibm.ipzvpd.VINI", {{"CC", Binary{}}}}}}};
573 
574                 common::utility::callPIM(move(pimObjMap));
575             }
576             else
577             {
578                 throw std::runtime_error("Path empty in Json");
579             }
580         }
581     }
582     catch (const GpioException& e)
583     {
584         PelAdditionalData additionalData{};
585         additionalData.emplace("DESCRIPTION", e.what());
586         createPEL(additionalData, PelSeverity::WARNING, errIntfForGpioError,
587                   nullptr);
588     }
589 }
590 
591 /**
592  * @brief Fills the Decorator.AssetTag property into the interfaces map
593  *
594  * This function should only be called in cases where we did not find a JSON
595  * symlink. A missing symlink in /var/lib will be considered as a factory reset
596  * and this function will be used to default the AssetTag property.
597  *
598  * @param interfaces A possibly pre-populated map of inetrfaces to properties.
599  * @param vpdMap A VPD map of the system VPD data.
600  */
601 static void fillAssetTag(inventory::InterfaceMap& interfaces,
602                          const Parsed& vpdMap)
603 {
604     // Read the system serial number and MTM
605     // Default asset tag is Server-MTM-System Serial
606     inventory::Interface assetIntf{
607         "xyz.openbmc_project.Inventory.Decorator.AssetTag"};
608     inventory::PropertyMap assetTagProps;
609     std::string defaultAssetTag =
610         std::string{"Server-"} + getKwVal(vpdMap, "VSYS", "TM") +
611         std::string{"-"} + getKwVal(vpdMap, "VSYS", "SE");
612     assetTagProps.emplace("AssetTag", defaultAssetTag);
613     insertOrMerge(interfaces, assetIntf, std::move(assetTagProps));
614 }
615 
616 /**
617  * @brief Set certain one time properties in the inventory
618  * Use this function to insert the Functional and Enabled properties into the
619  * inventory map. This function first checks if the object in question already
620  * has these properties hosted on D-Bus, if the property is already there, it is
621  * not modified, hence the name "one time". If the property is not already
622  * present, it will be added to the map with a suitable default value (true for
623  * Functional and Enabled)
624  *
625  * @param[in] object - The inventory D-Bus obejct without the inventory prefix.
626  * @param[inout] interfaces - Reference to a map of inventory interfaces to
627  * which the properties will be attached.
628  */
629 static void setOneTimeProperties(const std::string& object,
630                                  inventory::InterfaceMap& interfaces)
631 {
632     auto bus = sdbusplus::bus::new_default();
633     auto objectPath = INVENTORY_PATH + object;
634     auto prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
635                                     objectPath.c_str(),
636                                     "org.freedesktop.DBus.Properties", "Get");
637     prop.append("xyz.openbmc_project.State.Decorator.OperationalStatus");
638     prop.append("Functional");
639     try
640     {
641         auto result = bus.call(prop);
642     }
643     catch (const sdbusplus::exception::SdBusError& e)
644     {
645         // Treat as property unavailable
646         inventory::PropertyMap prop;
647         prop.emplace("Functional", true);
648         interfaces.emplace(
649             "xyz.openbmc_project.State.Decorator.OperationalStatus",
650             move(prop));
651     }
652     prop = bus.new_method_call("xyz.openbmc_project.Inventory.Manager",
653                                objectPath.c_str(),
654                                "org.freedesktop.DBus.Properties", "Get");
655     prop.append("xyz.openbmc_project.Object.Enable");
656     prop.append("Enabled");
657     try
658     {
659         auto result = bus.call(prop);
660     }
661     catch (const sdbusplus::exception::SdBusError& e)
662     {
663         // Treat as property unavailable
664         inventory::PropertyMap prop;
665         prop.emplace("Enabled", true);
666         interfaces.emplace("xyz.openbmc_project.Object.Enable", move(prop));
667     }
668 }
669 
670 /**
671  * @brief Prime the Inventory
672  * Prime the inventory by populating only the location code,
673  * type interface and the inventory object for the frus
674  * which are not system vpd fru.
675  *
676  * @param[in] jsObject - Reference to vpd inventory json object
677  * @param[in] vpdMap -  Reference to the parsed vpd map
678  *
679  * @returns Map of items in extraInterface.
680  */
681 template <typename T>
682 inventory::ObjectMap primeInventory(const nlohmann::json& jsObject,
683                                     const T& vpdMap)
684 {
685     inventory::ObjectMap objects;
686 
687     for (auto& itemFRUS : jsObject["frus"].items())
688     {
689         for (auto& itemEEPROM : itemFRUS.value())
690         {
691             // Take pre actions if needed
692             if (itemEEPROM.find("preAction") != itemEEPROM.end())
693             {
694                 preAction(jsObject, itemFRUS.key());
695             }
696 
697             inventory::InterfaceMap interfaces;
698             inventory::Object object(itemEEPROM.at("inventoryPath"));
699 
700             if ((itemFRUS.key() != systemVpdFilePath) &&
701                 !itemEEPROM.value("noprime", false))
702             {
703                 inventory::PropertyMap presProp;
704 
705                 // Do not populate Present property for frus whose
706                 // synthesized=true. synthesized=true says the fru VPD is
707                 // synthesized and owned by a separate component.
708                 // In some cases, the FRU has its own VPD, but still a separate
709                 // application handles the FRU's presence. So VPD parser skips
710                 // populating Present property by checking the JSON flag,
711                 // "handlePresence".
712                 if (!itemEEPROM.value("synthesized", false))
713                 {
714                     if (itemEEPROM.value("handlePresence", true))
715                     {
716                         presProp.emplace("Present", false);
717                         interfaces.emplace("xyz.openbmc_project.Inventory.Item",
718                                            presProp);
719                     }
720                 }
721 
722                 setOneTimeProperties(object, interfaces);
723                 if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
724                 {
725                     for (const auto& eI : itemEEPROM["extraInterfaces"].items())
726                     {
727                         inventory::PropertyMap props;
728                         if (eI.key() == IBM_LOCATION_CODE_INF)
729                         {
730                             if constexpr (std::is_same<T, Parsed>::value)
731                             {
732                                 for (auto& lC : eI.value().items())
733                                 {
734                                     auto propVal = expandLocationCode(
735                                         lC.value().get<string>(), vpdMap, true);
736 
737                                     props.emplace(move(lC.key()),
738                                                   move(propVal));
739                                     interfaces.emplace(XYZ_LOCATION_CODE_INF,
740                                                        props);
741                                     interfaces.emplace(move(eI.key()),
742                                                        move(props));
743                                 }
744                             }
745                         }
746                         else if (eI.key() ==
747                                  "xyz.openbmc_project.Inventory.Item")
748                         {
749                             for (auto& val : eI.value().items())
750                             {
751                                 if (val.key() == "PrettyName")
752                                 {
753                                     presProp.emplace(val.key(),
754                                                      val.value().get<string>());
755                                 }
756                             }
757                             // Use insert_or_assign here as we may already have
758                             // inserted the present property only earlier in
759                             // this function under this same interface.
760                             interfaces.insert_or_assign(eI.key(),
761                                                         move(presProp));
762                         }
763                         else
764                         {
765                             interfaces.emplace(move(eI.key()), move(props));
766                         }
767                     }
768                 }
769                 objects.emplace(move(object), move(interfaces));
770             }
771         }
772     }
773     return objects;
774 }
775 
776 /**
777  * @brief This API executes command to set environment variable
778  *        And then reboot the system
779  * @param[in] key   -env key to set new value
780  * @param[in] value -value to set.
781  */
782 void setEnvAndReboot(const string& key, const string& value)
783 {
784     // set env and reboot and break.
785     executeCmd("/sbin/fw_setenv", key, value);
786     log<level::INFO>("Rebooting BMC to pick up new device tree");
787     // make dbus call to reboot
788     auto bus = sdbusplus::bus::new_default_system();
789     auto method = bus.new_method_call(
790         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
791         "org.freedesktop.systemd1.Manager", "Reboot");
792     bus.call_noreply(method);
793 }
794 
795 /*
796  * @brief This API checks for env var fitconfig.
797  *        If not initialised OR updated as per the current system type,
798  *        update this env var and reboot the system.
799  *
800  * @param[in] systemType IM kwd in vpd tells about which system type it is.
801  * */
802 void setDevTreeEnv(const string& systemType)
803 {
804     // Init with default dtb
805     string newDeviceTree = "conf-aspeed-bmc-ibm-rainier-p1.dtb";
806     static const deviceTreeMap deviceTreeSystemTypeMap = {
807         {RAINIER_2U, "conf-aspeed-bmc-ibm-rainier-p1.dtb"},
808         {RAINIER_2U_V2, "conf-aspeed-bmc-ibm-rainier.dtb"},
809         {RAINIER_4U, "conf-aspeed-bmc-ibm-rainier-4u-p1.dtb"},
810         {RAINIER_4U_V2, "conf-aspeed-bmc-ibm-rainier-4u.dtb"},
811         {RAINIER_1S4U, "conf-aspeed-bmc-ibm-rainier-1s4u.dtb"},
812         {EVEREST, "conf-aspeed-bmc-ibm-everest.dtb"},
813         {EVEREST_V2, "conf-aspeed-bmc-ibm-everest.dtb"},
814         {BONNELL, "conf-aspeed-bmc-ibm-bonnell.dtb"}};
815 
816     if (deviceTreeSystemTypeMap.find(systemType) !=
817         deviceTreeSystemTypeMap.end())
818     {
819         newDeviceTree = deviceTreeSystemTypeMap.at(systemType);
820     }
821     else
822     {
823         // System type not supported
824         string err = "This System type not found/supported in dtb table " +
825                      systemType +
826                      ".Please check the HW and IM keywords in the system "
827                      "VPD.Breaking...";
828 
829         // map to hold additional data in case of logging pel
830         PelAdditionalData additionalData{};
831         additionalData.emplace("DESCRIPTION", err);
832         createPEL(additionalData, PelSeverity::WARNING,
833                   errIntfForInvalidSystemType, nullptr);
834         exit(-1);
835     }
836 
837     string readVarValue;
838     bool envVarFound = false;
839 
840     vector<string> output = executeCmd("/sbin/fw_printenv");
841     for (const auto& entry : output)
842     {
843         size_t pos = entry.find("=");
844         string key = entry.substr(0, pos);
845         if (key != "fitconfig")
846         {
847             continue;
848         }
849 
850         envVarFound = true;
851         if (pos + 1 < entry.size())
852         {
853             readVarValue = entry.substr(pos + 1);
854             if (readVarValue.find(newDeviceTree) != string::npos)
855             {
856                 // fitconfig is Updated. No action needed
857                 break;
858             }
859         }
860         // set env and reboot and break.
861         setEnvAndReboot(key, newDeviceTree);
862         exit(0);
863     }
864 
865     // check If env var Not found
866     if (!envVarFound)
867     {
868         setEnvAndReboot("fitconfig", newDeviceTree);
869     }
870 }
871 
872 /**
873  * @brief Parse the given EEPROM file.
874  *
875  * @param[in] vpdFilePath - Path of EEPROM file
876  * @param[in] js- Reference to vpd inventory json object
877  * @return Parsed VPD map
878  */
879 std::variant<KeywordVpdMap, openpower::vpd::Store>
880     parseVpdFile(const std::string& vpdFilePath, const nlohmann::json& js)
881 {
882     uint32_t vpdStartOffset = 0;
883     for (const auto& item : js["frus"][vpdFilePath])
884     {
885         if (item.find("offset") != item.end())
886         {
887             vpdStartOffset = item["offset"];
888             break;
889         }
890     }
891 
892     Binary vpdVector = getVpdDataInVector(js, vpdFilePath);
893 
894     ParserInterface* parser = ParserFactory::getParser(
895         vpdVector,
896         (pimPath + js["frus"][vpdFilePath][0]["inventoryPath"]
897                        .get_ref<const nlohmann::json::string_t&>()),
898         vpdFilePath, vpdStartOffset);
899 
900     auto parseResult = parser->parse();
901 
902     // release the parser object
903     ParserFactory::freeParser(parser);
904 
905     return parseResult;
906 }
907 
908 /*
909  * @brief This API retrieves the hardware backup in map
910  *
911  * @param[in] systemVpdBackupPath - The path that backs up the system VPD.
912  * @param[in] backupVpdInvPath - FRU inventory path.
913  * @param[in] js - JSON object.
914  * @param[out] backupVpdMap - An IPZ VPD map containing the parsed backup VPD.
915  *
916  * */
917 void getBackupVpdInMap(const string& systemVpdBackupPath,
918                        const string& backupVpdInvPath, const nlohmann::json& js,
919                        Parsed& backupVpdMap)
920 {
921     PelAdditionalData additionalData{};
922 
923     if (!fs::exists(systemVpdBackupPath))
924     {
925         string errorMsg = "Device path ";
926         errorMsg += systemVpdBackupPath;
927         errorMsg += " does not exist";
928 
929         additionalData.emplace("DESCRIPTION", errorMsg);
930 
931         additionalData.emplace("CALLOUT_INVENTORY_PATH",
932                                INVENTORY_PATH + backupVpdInvPath);
933 
934         createPEL(additionalData, PelSeverity::ERROR, errIntfForStreamFail,
935                   nullptr);
936     }
937     else
938     {
939         auto backupVpdParsedResult = parseVpdFile(systemVpdBackupPath, js);
940 
941         if (auto pVal = get_if<Store>(&backupVpdParsedResult))
942         {
943             backupVpdMap = pVal->getVpdMap();
944         }
945         else
946         {
947             std::cerr << "Invalid format of VPD in back up. Restore aborted."
948                       << std::endl;
949         }
950     }
951 }
952 
953 void updateVpdDataOnHw(const std::string& vpdFilePath, nlohmann::json& js,
954                        const std::string& recName, const std::string& kwName,
955                        const Binary& kwdData)
956 {
957     const std::string& fruInvPath =
958         js["frus"][vpdFilePath][0]["inventoryPath"]
959             .get_ref<const nlohmann::json::string_t&>();
960 
961     EditorImpl edit(vpdFilePath, js, recName, kwName, fruInvPath);
962 
963     uint32_t offset = 0;
964     // Setup offset, if any
965     for (const auto& item : js["frus"][vpdFilePath])
966     {
967         if (item.find("offset") != item.end())
968         {
969             offset = item["offset"];
970             break;
971         }
972     }
973 
974     // update keyword data on to EEPROM file
975     // Note: Updating keyword data on cache is
976     // handled via PIM Notify call hence passing
977     // the updCache flag value as false here.
978     edit.updateKeyword(kwdData, offset, false);
979 }
980 
981 /**
982  * @brief API to check if we need to restore system VPD
983  * This functionality is only applicable for IPZ VPD data.
984 
985  * @param[in] vpdMap - IPZ vpd map
986  * @param[in] objectPath - Object path for the FRU
987  * @param[in] js - JSON Object
988  * @param[in] isBackupOnCache - Denotes whether the backup is on cache/hardware
989  */
990 void restoreSystemVPD(Parsed& vpdMap, const string& objectPath,
991                       nlohmann::json& js, bool isBackupOnCache = true)
992 {
993     std::string systemVpdBackupPath{};
994     std::string backupVpdInvPath{};
995     Parsed backupVpdMap{};
996 
997     if (!isBackupOnCache)
998     {
999         // Get the value of systemvpdBackupPath field from json
1000         systemVpdBackupPath = js["frus"][systemVpdFilePath].at(0).value(
1001             "systemVpdBackupPath", "");
1002 
1003         backupVpdInvPath = js["frus"][systemVpdBackupPath][0]["inventoryPath"]
1004                                .get_ref<const nlohmann::json::string_t&>();
1005 
1006         getBackupVpdInMap(systemVpdBackupPath, backupVpdInvPath, js,
1007                           backupVpdMap);
1008 
1009         if (backupVpdMap.empty())
1010         {
1011             std::cerr << "Backup VPD map is empty" << std::endl;
1012             return;
1013         }
1014     }
1015 
1016     for (const auto& systemRecKwdPair : svpdKwdMap)
1017     {
1018         const string& recordName = systemRecKwdPair.first;
1019         auto it = vpdMap.find(recordName);
1020 
1021         // check if record is found in map we got by parser
1022         if (it != vpdMap.end())
1023         {
1024             const auto& kwdListForRecord = systemRecKwdPair.second;
1025             for (const auto& keywordInfo : kwdListForRecord)
1026             {
1027                 const auto keywordName = get<0>(keywordInfo);
1028 
1029                 DbusPropertyMap& kwdValMap = it->second;
1030                 auto iterator = kwdValMap.find(keywordName);
1031 
1032                 if (iterator != kwdValMap.end())
1033                 {
1034                     string& kwdValue = iterator->second;
1035 
1036                     std::string backupValue{};
1037                     const auto& defaultValue = get<1>(keywordInfo);
1038                     const auto& backupVpdRecName = get<4>(keywordInfo);
1039                     const auto& backupVpdKwName = get<5>(keywordInfo);
1040 
1041                     // If the 'isBackupOnCache' flag is false, we need
1042                     // to backup the systemVPD on the specified fru's eeprom
1043                     // path or restore it from the specified fru's eeprom path.
1044                     if (isBackupOnCache)
1045                     {
1046                         // check bus data
1047                         backupValue = readBusProperty(
1048                             objectPath, ipzVpdInf + recordName, keywordName);
1049                     }
1050                     else
1051                     {
1052                         backupValue = getKwVal(backupVpdMap, backupVpdRecName,
1053                                                backupVpdKwName);
1054 
1055                         if (backupValue.empty())
1056                         {
1057                             string errorMsg{};
1058                             if (backupVpdMap.find(backupVpdRecName) ==
1059                                 backupVpdMap.end())
1060                             {
1061                                 errorMsg = backupVpdRecName +
1062                                            " Record does not exist in "
1063                                            "the EEPROM file ";
1064                             }
1065                             else
1066                             {
1067                                 errorMsg = backupVpdKwName +
1068                                            " Keyword not found or empty.";
1069                             }
1070 
1071                             errorMsg += systemVpdBackupPath;
1072 
1073                             PelAdditionalData additionalData;
1074                             additionalData.emplace("DESCRIPTION", errorMsg);
1075 
1076                             createPEL(additionalData, PelSeverity::ERROR,
1077                                       errIntfForInvalidVPD, nullptr);
1078 
1079                             continue;
1080                         }
1081                     }
1082 
1083                     Binary backupDataInBinary(backupValue.begin(),
1084                                               backupValue.end());
1085 
1086                     Binary kwdDataInBinary(kwdValue.begin(), kwdValue.end());
1087 
1088                     if (backupDataInBinary != defaultValue)
1089                     {
1090                         if (kwdDataInBinary != defaultValue)
1091                         {
1092                             // both the data are present, check for mismatch
1093                             if (backupValue != kwdValue)
1094                             {
1095                                 string errMsg = "Mismatch found between backup "
1096                                                 "and primary VPD for record: ";
1097                                 errMsg += (*it).first;
1098                                 errMsg += " and keyword: ";
1099                                 errMsg += keywordName;
1100 
1101                                 std::ostringstream busStream;
1102                                 for (uint16_t byte : backupValue)
1103                                 {
1104                                     busStream << std::setfill('0')
1105                                               << std::setw(2) << std::hex
1106                                               << "0x" << byte << " ";
1107                                 }
1108 
1109                                 std::ostringstream vpdStream;
1110                                 for (uint16_t byte : kwdValue)
1111                                 {
1112                                     vpdStream << std::setfill('0')
1113                                               << std::setw(2) << std::hex
1114                                               << "0x" << byte << " ";
1115                                 }
1116 
1117                                 // data mismatch
1118                                 PelAdditionalData additionalData;
1119 
1120                                 additionalData.emplace("DESCRIPTION", errMsg);
1121                                 additionalData.emplace(
1122                                     "Value read from Backup: ",
1123                                     busStream.str());
1124                                 additionalData.emplace(
1125                                     "Value read from Primary: ",
1126                                     vpdStream.str());
1127 
1128                                 createPEL(additionalData, PelSeverity::WARNING,
1129                                           errIntfForVPDMismatch, nullptr);
1130 
1131                                 if (!isBackupOnCache)
1132                                 {
1133                                     // Backing up or restoring from a hardware
1134                                     // path does not requires copying the backup
1135                                     // data to the VPD map, as this will result
1136                                     // in a mismatch between the primary VPD and
1137                                     // its cache.
1138                                     continue;
1139                                 }
1140                             }
1141                             else
1142                             {
1143                                 // both the backup and primary data is
1144                                 // non-default and same. Nothing needs to be
1145                                 // done.
1146                                 continue;
1147                             }
1148                         }
1149 
1150                         // If the backup is on the cache we need to copy the
1151                         // backup data to the VPD map to ensure there is no
1152                         // mimatch b/n them. So if backup data is not default,
1153                         // then irrespective of primary data(default or other
1154                         // than backup), copy the backup data to vpd map as we
1155                         // don't need to change the backup data in either case
1156                         // in the process of restoring system vpd.
1157                         kwdValue = backupValue;
1158 
1159                         // If the backup data is on the base panel the restoring
1160                         // of Backup VPD on to the system backplane VPD
1161                         // file is done here not through the VPD manager code
1162                         // path. This is to have the logic of restoring data on
1163                         // to the cache & hardware in the same code path.
1164                         if (!isBackupOnCache)
1165                         {
1166                             // copy backup VPD on to system backplane
1167                             // EEPROM  file.
1168                             updateVpdDataOnHw(systemVpdFilePath, js, recordName,
1169                                               keywordName, backupDataInBinary);
1170                         }
1171                     }
1172                     else if (kwdDataInBinary == defaultValue &&
1173                              get<2>(keywordInfo)) // Check isPELRequired is true
1174                     {
1175                         string errMsg = "Found default value on both backup "
1176                                         "and primary VPD for record: ";
1177                         errMsg += (*it).first;
1178                         errMsg += " and keyword: ";
1179                         errMsg += keywordName;
1180                         errMsg += ". Update primary VPD.";
1181 
1182                         // mfg default on both backup and primary, log PEL
1183                         PelAdditionalData additionalData;
1184                         additionalData.emplace("DESCRIPTION", errMsg);
1185 
1186                         createPEL(additionalData, PelSeverity::ERROR,
1187                                   errIntfForVPDDefault, nullptr);
1188 
1189                         continue;
1190                     }
1191                     else if ((kwdDataInBinary != defaultValue) &&
1192                              (!isBackupOnCache))
1193                     {
1194                         // update primary VPD on to backup VPD file
1195                         updateVpdDataOnHw(systemVpdBackupPath, js,
1196                                           backupVpdRecName, backupVpdKwName,
1197                                           kwdDataInBinary);
1198 
1199                         // copy primary VPD to backup VPD to publish on
1200                         // DBus
1201                         backupVpdMap.find(backupVpdRecName)
1202                             ->second.find(backupVpdKwName)
1203                             ->second = kwdValue;
1204                     }
1205                 }
1206             }
1207         }
1208     }
1209 }
1210 
1211 /**
1212  * @brief This checks for is this FRU a processor
1213  *        And if yes, then checks for is this primary
1214  *
1215  * @param[in] js- vpd json to get the information about this FRU
1216  * @param[in] filePath- FRU vpd
1217  *
1218  * @return true/false
1219  */
1220 bool isThisPrimaryProcessor(nlohmann::json& js, const string& filePath)
1221 {
1222     bool isProcessor = false;
1223     bool isPrimary = false;
1224 
1225     for (const auto& item : js["frus"][filePath])
1226     {
1227         if (item.find("extraInterfaces") != item.end())
1228         {
1229             for (const auto& eI : item["extraInterfaces"].items())
1230             {
1231                 if (eI.key().find("Inventory.Item.Cpu") != string::npos)
1232                 {
1233                     isProcessor = true;
1234                 }
1235             }
1236         }
1237 
1238         if (isProcessor)
1239         {
1240             string cpuType = item.value("cpuType", "");
1241             if (cpuType == "primary")
1242             {
1243                 isPrimary = true;
1244             }
1245         }
1246     }
1247 
1248     return (isProcessor && isPrimary);
1249 }
1250 
1251 /**
1252  * @brief This finds DIMM vpd in vpd json and enables them by binding the device
1253  *        driver
1254  * @param[in] js- vpd json to iterate through and take action if it is DIMM
1255  */
1256 void doEnableAllDimms(nlohmann::json& js)
1257 {
1258     // iterate over each fru
1259     for (const auto& eachFru : js["frus"].items())
1260     {
1261         // skip the driver binding if eeprom already exists
1262         if (fs::exists(eachFru.key()))
1263         {
1264             continue;
1265         }
1266 
1267         for (const auto& eachInventory : eachFru.value())
1268         {
1269             if (eachInventory.find("extraInterfaces") != eachInventory.end())
1270             {
1271                 for (const auto& eI : eachInventory["extraInterfaces"].items())
1272                 {
1273                     if (eI.key().find("Inventory.Item.Dimm") != string::npos)
1274                     {
1275                         string dimmVpd = eachFru.key();
1276                         // fetch it from
1277                         // "/sys/bus/i2c/drivers/at24/414-0050/eeprom"
1278 
1279                         regex matchPatern("([0-9]+-[0-9]{4})");
1280                         smatch matchFound;
1281                         if (regex_search(dimmVpd, matchFound, matchPatern))
1282                         {
1283                             vector<string> i2cReg;
1284                             boost::split(i2cReg, matchFound.str(0),
1285                                          boost::is_any_of("-"));
1286 
1287                             // remove 0s from begining
1288                             const regex pattern("^0+(?!$)");
1289                             for (auto& i : i2cReg)
1290                             {
1291                                 i = regex_replace(i, pattern, "");
1292                             }
1293 
1294                             // For ISDIMM which uses ee1004 driver
1295                             // the below is done
1296                             size_t stringFound = dimmVpd.find("ee1004");
1297                             if (stringFound != string::npos)
1298                             {
1299                                 // echo ee1004 0x50 >
1300                                 // /sys/bus/i2c/devices/i2c-110/new_device
1301                                 string cmnd = "echo ee1004 0x" + i2cReg[1] +
1302                                               " > /sys/bus/i2c/devices/i2c-" +
1303                                               i2cReg[0] + "/new_device";
1304                                 executeCmd(cmnd);
1305                             }
1306                             else if (i2cReg.size() == 2)
1307                             {
1308                                 // echo 24c32 0x50 >
1309                                 // /sys/bus/i2c/devices/i2c-16/new_device
1310                                 string cmnd = "echo 24c32 0x" + i2cReg[1] +
1311                                               " > /sys/bus/i2c/devices/i2c-" +
1312                                               i2cReg[0] + "/new_device";
1313                                 executeCmd(cmnd);
1314                             }
1315                         }
1316                     }
1317                 }
1318             }
1319         }
1320     }
1321 }
1322 
1323 /**
1324  * @brief Check if the given CPU is an IO only chip.
1325  * The CPU is termed as IO, whose all of the cores are bad and can never be
1326  * used. Those CPU chips can be used for IO purpose like connecting PCIe devices
1327  * etc., The CPU whose every cores are bad, can be identified from the CP00
1328  * record's PG keyword, only if all of the 8 EQs' value equals 0xE7F9FF. (1EQ
1329  * has 4 cores grouped together by sharing its cache memory.)
1330  * @param [in] pgKeyword - PG Keyword of CPU.
1331  * @return true if the given cpu is an IO, false otherwise.
1332  */
1333 static bool isCPUIOGoodOnly(const string& pgKeyword)
1334 {
1335     const unsigned char io[] = {0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9,
1336                                 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7,
1337                                 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
1338     // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
1339     // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
1340     // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
1341     // IO.
1342     if (memcmp(io, pgKeyword.data() + 97, 24) == 0)
1343     {
1344         return true;
1345     }
1346 
1347     // The CPU is not an IO
1348     return false;
1349 }
1350 
1351 /**
1352  * @brief Function to bring MUX out of idle state
1353  *
1354  *        This finds All the MUX defined in the system json and enables
1355  *        them by setting the holdidle parameter to 0.
1356  * @param[in] js- system json to iterate through and take action
1357  */
1358 void doEnableAllMuxChips(const nlohmann::json& js)
1359 {
1360     // Do we have the mandatory "muxes" section?
1361     if (js.find("muxes") != js.end())
1362     {
1363         std::cout << "Enabling all the MUX on the system " << std::endl;
1364         // iterate over each MUX detail and enable them
1365         for (const auto& item : js["muxes"])
1366         {
1367             if (item.find("holdidlepath") != item.end())
1368             {
1369                 const std::string& holdidle = item["holdidlepath"];
1370                 std::cout << "Setting holdidle state for " << holdidle
1371                           << "to 0 " << std::endl;
1372                 string cmd = "echo 0 > " + holdidle;
1373                 executeCmd(cmd);
1374             }
1375         }
1376         std::cout << "Completed enabling all the MUX on the system "
1377                   << std::endl;
1378     }
1379     else
1380     {
1381         std::cout << "No MUX was defined for the system" << std::endl;
1382     }
1383 }
1384 
1385 /**
1386  * @brief Populate Dbus.
1387  * This method invokes all the populateInterface functions
1388  * and notifies PIM about dbus object.
1389  * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
1390  * input.
1391  * @param[in] js - Inventory json object
1392  * @param[in] filePath - Path of the vpd file
1393  * @param[in] preIntrStr - Interface string
1394  */
1395 template <typename T>
1396 static void populateDbus(T& vpdMap, nlohmann::json& js, const string& filePath)
1397 {
1398     inventory::InterfaceMap interfaces;
1399     inventory::ObjectMap objects;
1400     inventory::PropertyMap prop;
1401     string ccinFromVpd;
1402 
1403     bool isSystemVpd = (filePath == systemVpdFilePath);
1404     if constexpr (is_same<T, Parsed>::value)
1405     {
1406         ccinFromVpd = getKwVal(vpdMap, "VINI", "CC");
1407         transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
1408                   ::toupper);
1409 
1410         if (isSystemVpd)
1411         {
1412             string mboardPath =
1413                 js["frus"][filePath].at(0).value("inventoryPath", "");
1414 
1415             // Get the value of systemvpdBackupPath field from json
1416             const std::string& systemVpdBackupPath =
1417                 js["frus"][filePath].at(0).value("systemVpdBackupPath", "");
1418 
1419             if (systemVpdBackupPath.empty())
1420             {
1421                 std::vector<std::string> interfaces = {motherBoardInterface};
1422                 // call mapper to check for object path creation
1423                 MapperResponse subTree =
1424                     getObjectSubtreeForInterfaces(pimPath, 0, interfaces);
1425 
1426                 // Attempt system VPD restore if we have a motherboard
1427                 // object in the inventory.
1428                 if ((subTree.size() != 0) &&
1429                     (subTree.find(pimPath + mboardPath) != subTree.end()))
1430                 {
1431                     restoreSystemVPD(vpdMap, mboardPath, js);
1432                 }
1433                 else
1434                 {
1435                     log<level::ERR>("No object path found");
1436                 }
1437             }
1438             else
1439             {
1440                 restoreSystemVPD(vpdMap, mboardPath, js, false);
1441             }
1442         }
1443         else
1444         {
1445             // check if it is processor vpd.
1446             auto isPrimaryCpu = isThisPrimaryProcessor(js, filePath);
1447 
1448             if (isPrimaryCpu)
1449             {
1450                 auto ddVersion = getKwVal(vpdMap, "CRP0", "DD");
1451 
1452                 auto chipVersion = atoi(ddVersion.substr(1, 2).c_str());
1453 
1454                 if (chipVersion >= 2)
1455                 {
1456                     doEnableAllDimms(js);
1457                     // Sleep for a few seconds to let the DIMM parses start
1458                     using namespace std::chrono_literals;
1459                     std::this_thread::sleep_for(5s);
1460                 }
1461             }
1462         }
1463     }
1464 
1465     auto processFactoryReset = false;
1466 
1467     if (isSystemVpd)
1468     {
1469         string systemJsonName{};
1470         if constexpr (is_same<T, Parsed>::value)
1471         {
1472             // pick the right system json
1473             systemJsonName = getSystemsJson(vpdMap);
1474         }
1475 
1476         fs::path target = systemJsonName;
1477         fs::path link = INVENTORY_JSON_SYM_LINK;
1478 
1479         // If the symlink does not exist, we treat that as a factory reset
1480         processFactoryReset = !fs::exists(INVENTORY_JSON_SYM_LINK);
1481 
1482         // Create the directory for hosting the symlink
1483         fs::create_directories(VPD_FILES_PATH);
1484         // unlink the symlink previously created (if any)
1485         remove(INVENTORY_JSON_SYM_LINK);
1486         // create a new symlink based on the system
1487         fs::create_symlink(target, link);
1488 
1489         // Reloading the json
1490         ifstream inventoryJson(link);
1491         js = json::parse(inventoryJson);
1492         inventoryJson.close();
1493     }
1494 
1495     for (const auto& item : js["frus"][filePath])
1496     {
1497         const auto& objectPath = item["inventoryPath"];
1498         sdbusplus::message::object_path object(objectPath);
1499 
1500         vector<string> ccinList;
1501         if (item.find("ccin") != item.end())
1502         {
1503             for (const auto& cc : item["ccin"])
1504             {
1505                 string ccin = cc;
1506                 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
1507                 ccinList.push_back(ccin);
1508             }
1509         }
1510 
1511         if (!ccinFromVpd.empty() && !ccinList.empty() &&
1512             (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
1513              ccinList.end()))
1514         {
1515             continue;
1516         }
1517 
1518         if ((isSystemVpd) || (item.value("noprime", false)))
1519         {
1520             // Populate one time properties for the system VPD and its sub-frus
1521             // and for other non-primeable frus.
1522             // For the remaining FRUs, this will get handled as a part of
1523             // priming the inventory.
1524             setOneTimeProperties(objectPath, interfaces);
1525         }
1526 
1527         // Populate the VPD keywords and the common interfaces only if we
1528         // are asked to inherit that data from the VPD, else only add the
1529         // extraInterfaces.
1530         if (item.value("inherit", true))
1531         {
1532             if constexpr (is_same<T, Parsed>::value)
1533             {
1534                 // Each record in the VPD becomes an interface and all
1535                 // keyword within the record are properties under that
1536                 // interface.
1537                 for (const auto& record : vpdMap)
1538                 {
1539                     populateFruSpecificInterfaces(
1540                         record.second, ipzVpdInf + record.first, interfaces);
1541                 }
1542             }
1543             else if constexpr (is_same<T, KeywordVpdMap>::value)
1544             {
1545                 populateFruSpecificInterfaces(vpdMap, kwdVpdInf, interfaces);
1546             }
1547             if (js.find("commonInterfaces") != js.end())
1548             {
1549                 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
1550                                    isSystemVpd);
1551             }
1552         }
1553         else
1554         {
1555             // Check if we have been asked to inherit specific record(s)
1556             if constexpr (is_same<T, Parsed>::value)
1557             {
1558                 if (item.find("copyRecords") != item.end())
1559                 {
1560                     for (const auto& record : item["copyRecords"])
1561                     {
1562                         const string& recordName = record;
1563                         if (vpdMap.find(recordName) != vpdMap.end())
1564                         {
1565                             populateFruSpecificInterfaces(
1566                                 vpdMap.at(recordName), ipzVpdInf + recordName,
1567                                 interfaces);
1568                         }
1569                     }
1570                 }
1571             }
1572         }
1573         // Populate interfaces and properties that are common to every FRU
1574         // and additional interface that might be defined on a per-FRU
1575         // basis.
1576         if (item.find("extraInterfaces") != item.end())
1577         {
1578             populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
1579                                isSystemVpd);
1580             if constexpr (is_same<T, Parsed>::value)
1581             {
1582                 if (item["extraInterfaces"].find(
1583                         "xyz.openbmc_project.Inventory.Item.Cpu") !=
1584                     item["extraInterfaces"].end())
1585                 {
1586                     if (isCPUIOGoodOnly(getKwVal(vpdMap, "CP00", "PG")))
1587                     {
1588                         interfaces[invItemIntf]["PrettyName"] = "IO Module";
1589                     }
1590                 }
1591             }
1592         }
1593 
1594         // embedded property(true or false) says whether the subfru is embedded
1595         // into the parent fru (or) not. VPD sets Present property only for
1596         // embedded frus. If the subfru is not an embedded FRU, the subfru may
1597         // or may not be physically present. Those non embedded frus will always
1598         // have Present=false irrespective of its physical presence or absence.
1599         // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
1600         // Present to true for such sub frus.
1601         // Eg: ethernet port is embedded into bmc card. So set Present to true
1602         // for such sub frus. Also donot populate present property for embedded
1603         // subfru which is synthesized. Currently there is no subfru which are
1604         // both embedded and synthesized. But still the case is handled here.
1605         if ((item.value("embedded", true)) &&
1606             (!item.value("synthesized", false)))
1607         {
1608             // Check if its required to handle presence for this FRU.
1609             if (item.value("handlePresence", true))
1610             {
1611                 inventory::PropertyMap presProp;
1612                 presProp.emplace("Present", true);
1613                 insertOrMerge(interfaces, invItemIntf, move(presProp));
1614             }
1615         }
1616 
1617         if constexpr (is_same<T, Parsed>::value)
1618         {
1619             // Restore asset tag, if needed
1620             if (processFactoryReset && objectPath == "/system")
1621             {
1622                 fillAssetTag(interfaces, vpdMap);
1623             }
1624         }
1625 
1626         objects.emplace(move(object), move(interfaces));
1627     }
1628 
1629     if (isSystemVpd)
1630     {
1631         inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
1632         objects.insert(primeObject.begin(), primeObject.end());
1633 
1634         // set the U-boot environment variable for device-tree
1635         if constexpr (is_same<T, Parsed>::value)
1636         {
1637             setDevTreeEnv(fs::path(getSystemsJson(vpdMap)).filename());
1638         }
1639     }
1640 
1641     // Notify PIM
1642     common::utility::callPIM(move(objects));
1643 }
1644 
1645 int main(int argc, char** argv)
1646 {
1647     int rc = 0;
1648     json js{};
1649     Binary vpdVector{};
1650     string file{};
1651     string driver{};
1652     // map to hold additional data in case of logging pel
1653     PelAdditionalData additionalData{};
1654 
1655     // this is needed to hold base fru inventory path in case there is ECC or
1656     // vpd exception while parsing the file
1657     std::string baseFruInventoryPath = {};
1658 
1659     // It holds the backup EEPROM file path for the system backplane's critical
1660     // data
1661     std::string systemVpdBackupPath{};
1662 
1663     // It holds the inventory path of backup EEPROM file
1664     std::string backupVpdInvPath{};
1665 
1666     bool isSystemVpd = false;
1667 
1668     // severity for PEL
1669     PelSeverity pelSeverity = PelSeverity::WARNING;
1670 
1671     try
1672     {
1673         App app{"ibm-read-vpd - App to read IPZ/Jedec format VPD, parse it and "
1674                 "store it in DBUS"};
1675 
1676         app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
1677             ->required();
1678 
1679         app.add_option("--driver", driver,
1680                        "Driver used by kernel (at24,at25,ee1004)")
1681             ->required();
1682 
1683         CLI11_PARSE(app, argc, argv);
1684 
1685         // PEL severity should be ERROR in case of any system VPD failure
1686         if (file == systemVpdFilePath)
1687         {
1688             pelSeverity = PelSeverity::ERROR;
1689             isSystemVpd = true;
1690         }
1691 
1692         // Check if input file is not empty.
1693         if ((file.empty()) || (driver.empty()))
1694         {
1695             std::cerr << "Encountered empty input parameter file [" << file
1696                       << "] driver [" << driver << "]" << std::endl;
1697             return 0;
1698         }
1699 
1700         // Check if currently supported driver or not
1701         if ((driver != at24driver) && (driver != at25driver) &&
1702             (driver != ee1004driver))
1703         {
1704             std::cerr << "The driver [" << driver << "] is not supported."
1705                       << std::endl;
1706             return 0;
1707         }
1708 
1709         auto jsonToParse = INVENTORY_JSON_DEFAULT;
1710 
1711         // If the symlink exists, it means it has been setup for us, switch the
1712         // path
1713         if (fs::exists(INVENTORY_JSON_SYM_LINK))
1714         {
1715             jsonToParse = INVENTORY_JSON_SYM_LINK;
1716         }
1717 
1718         // Make sure that the file path we get is for a supported EEPROM
1719         ifstream inventoryJson(jsonToParse);
1720         if (!inventoryJson)
1721         {
1722             throw(VpdJsonException("Failed to access Json path", jsonToParse));
1723         }
1724 
1725         try
1726         {
1727             js = json::parse(inventoryJson);
1728         }
1729         catch (const json::parse_error& ex)
1730         {
1731             throw(VpdJsonException("Json parsing failed", jsonToParse));
1732         }
1733 
1734         // Do we have the mandatory "frus" section?
1735         if (js.find("frus") == js.end())
1736         {
1737             throw(VpdJsonException("FRUs section not found in JSON",
1738                                    jsonToParse));
1739         }
1740 
1741         // Check if it's a udev path - patterned as(/ahb/ahb:apb/ahb:apb:bus@)
1742         if (file.find("/ahb:apb") != string::npos)
1743         {
1744             // Translate udev path to a generic /sys/bus/.. file path.
1745             udevToGenericPath(file, driver);
1746 
1747             if ((js["frus"].find(file) != js["frus"].end()) &&
1748                 (file == systemVpdFilePath))
1749             {
1750                 std::cout << "We have already collected system VPD, skiping."
1751                           << std::endl;
1752                 return 0;
1753             }
1754         }
1755 
1756         // Enable all mux which are used for connecting to the i2c on the pcie
1757         // slots for pcie cards. These are not enabled by kernel due to an issue
1758         // seen with Castello cards, where the i2c line hangs on a probe.
1759         // To run it only once have kept it under System vpd check.
1760         // we need to run this on all BMC reboots so kept here
1761         if (file == systemVpdFilePath)
1762         {
1763             doEnableAllMuxChips(js);
1764         }
1765 
1766         if (file.empty())
1767         {
1768             std::cerr << "The EEPROM path <" << file << "> is not valid.";
1769             return 0;
1770         }
1771         if (js["frus"].find(file) == js["frus"].end())
1772         {
1773             std::cerr << "The EEPROM path [" << file
1774                       << "] is not found in the json." << std::endl;
1775             return 0;
1776         }
1777 
1778         if (!fs::exists(file))
1779         {
1780             std::cout << "Device path: " << file
1781                       << " does not exist. Spurious udev event? Exiting."
1782                       << std::endl;
1783             return 0;
1784         }
1785 
1786         // In case of system VPD it will already be filled, Don't have to
1787         // overwrite that.
1788         if (baseFruInventoryPath.empty())
1789         {
1790             baseFruInventoryPath = js["frus"][file][0]["inventoryPath"];
1791         }
1792 
1793         // Check if we can read the VPD file based on the power state
1794         // We skip reading VPD when the power is ON in two scenarios:
1795         // 1) The eeprom we are trying to read is that of the system VPD and the
1796         // JSON symlink is already setup (the symlink's existence tells us we
1797         // are not coming out of a factory reset)
1798         // 2) The JSON tells us that the FRU EEPROM cannot be
1799         // read when we are powered ON.
1800         if (js["frus"][file].at(0).value("powerOffOnly", false) ||
1801             (file == systemVpdFilePath && fs::exists(INVENTORY_JSON_SYM_LINK)))
1802         {
1803             if ("xyz.openbmc_project.State.Chassis.PowerState.On" ==
1804                 getPowerState())
1805             {
1806                 std::cout << "This VPD cannot be read when power is ON"
1807                           << std::endl;
1808                 return 0;
1809             }
1810         }
1811 
1812         // Check if this VPD should be recollected at all
1813         if (!needsRecollection(js, file))
1814         {
1815             std::cout << "Skip VPD recollection for: " << file << std::endl;
1816             return 0;
1817         }
1818 
1819         try
1820         {
1821             variant<KeywordVpdMap, Store> parseResult;
1822             parseResult = parseVpdFile(file, js);
1823 
1824             if (isSystemVpd)
1825             {
1826                 // Get the value of systemVpdBackupPath field from json
1827                 systemVpdBackupPath = js["frus"][systemVpdFilePath].at(0).value(
1828                     "systemVpdBackupPath", "");
1829 
1830                 if (!systemVpdBackupPath.empty())
1831                 {
1832                     backupVpdInvPath =
1833                         js["frus"][systemVpdBackupPath][0]["inventoryPath"]
1834                             .get_ref<const nlohmann::json::string_t&>();
1835                 }
1836             }
1837 
1838             if (auto pVal = get_if<Store>(&parseResult))
1839             {
1840                 populateDbus(pVal->getVpdMap(), js, file);
1841             }
1842             else if (auto pVal = get_if<KeywordVpdMap>(&parseResult))
1843             {
1844                 populateDbus(*pVal, js, file);
1845             }
1846         }
1847         catch (const exception& e)
1848         {
1849             if (!systemVpdBackupPath.empty())
1850             {
1851                 file = systemVpdBackupPath;
1852                 baseFruInventoryPath = backupVpdInvPath;
1853             }
1854 
1855             executePostFailAction(js, file);
1856             throw;
1857         }
1858     }
1859     catch (const VpdJsonException& ex)
1860     {
1861         additionalData.emplace("JSON_PATH", ex.getJsonPath());
1862         additionalData.emplace("DESCRIPTION", ex.what());
1863         createPEL(additionalData, pelSeverity, errIntfForJsonFailure, nullptr);
1864 
1865         std::cerr << ex.what() << "\n";
1866         rc = -1;
1867     }
1868     catch (const VpdEccException& ex)
1869     {
1870         additionalData.emplace("DESCRIPTION", "ECC check failed");
1871         additionalData.emplace("CALLOUT_INVENTORY_PATH",
1872                                INVENTORY_PATH + baseFruInventoryPath);
1873         createPEL(additionalData, pelSeverity, errIntfForEccCheckFail, nullptr);
1874 
1875         if (systemVpdBackupPath.empty())
1876         {
1877             dumpBadVpd(file, vpdVector);
1878         }
1879 
1880         std::cerr << ex.what() << "\n";
1881         rc = -1;
1882     }
1883     catch (const VpdDataException& ex)
1884     {
1885         if (isThisPcieOnPass1planar(js, file))
1886         {
1887             std::cout << "Pcie_device  [" << file
1888                       << "]'s VPD is not valid on PASS1 planar.Ignoring.\n";
1889             rc = 0;
1890         }
1891         else if (!(isPresent(js, file).value_or(true)))
1892         {
1893             std::cout << "FRU at: " << file
1894                       << " is not detected present. Ignore parser error.\n";
1895             rc = 0;
1896         }
1897         else
1898         {
1899             string errorMsg =
1900                 "VPD file is either empty or invalid. Parser failed for [";
1901             errorMsg += file;
1902             errorMsg += "], with error = " + std::string(ex.what());
1903 
1904             additionalData.emplace("DESCRIPTION", errorMsg);
1905             additionalData.emplace("CALLOUT_INVENTORY_PATH",
1906                                    INVENTORY_PATH + baseFruInventoryPath);
1907             createPEL(additionalData, pelSeverity, errIntfForInvalidVPD,
1908                       nullptr);
1909 
1910             rc = -1;
1911         }
1912     }
1913     catch (const exception& e)
1914     {
1915         dumpBadVpd(file, vpdVector);
1916         std::cerr << e.what() << "\n";
1917         rc = -1;
1918     }
1919 
1920     return rc;
1921 }
1922