xref: /openbmc/openpower-vpd-parser/vpd-manager/src/worker.cpp (revision 3e1cb49d2d469b79cafbd6372d071acb66ee612e)
1 #include "config.h"
2 
3 #include "worker.hpp"
4 
5 #include "backup_restore.hpp"
6 #include "configuration.hpp"
7 #include "constants.hpp"
8 #include "event_logger.hpp"
9 #include "exceptions.hpp"
10 #include "logger.hpp"
11 #include "parser.hpp"
12 #include "parser_factory.hpp"
13 #include "parser_interface.hpp"
14 
15 #include <utility/dbus_utility.hpp>
16 #include <utility/json_utility.hpp>
17 #include <utility/vpd_specific_utility.hpp>
18 
19 #include <filesystem>
20 #include <fstream>
21 #include <future>
22 #include <typeindex>
23 #include <unordered_set>
24 
25 namespace vpd
26 {
27 
28 Worker::Worker(std::string pathToConfigJson, uint8_t i_maxThreadCount) :
29     m_configJsonPath(pathToConfigJson), m_semaphore(i_maxThreadCount)
30 {
31     // Implies the processing is based on some config JSON
32     if (!m_configJsonPath.empty())
33     {
34         // Check if symlink is already there to confirm fresh boot/factory
35         // reset.
36         if (std::filesystem::exists(INVENTORY_JSON_SYM_LINK))
37         {
38             logging::logMessage("Sym Link already present");
39             m_configJsonPath = INVENTORY_JSON_SYM_LINK;
40             m_isSymlinkPresent = true;
41         }
42 
43         try
44         {
45             m_parsedJson = jsonUtility::getParsedJson(m_configJsonPath);
46 
47             // check for mandatory fields at this point itself.
48             if (!m_parsedJson.contains("frus"))
49             {
50                 throw std::runtime_error("Mandatory tag(s) missing from JSON");
51             }
52         }
53         catch (const std::exception& ex)
54         {
55             throw(JsonException(ex.what(), m_configJsonPath));
56         }
57     }
58     else
59     {
60         logging::logMessage("Processing in not based on any config JSON");
61     }
62 }
63 
64 static std::string readFitConfigValue()
65 {
66     std::vector<std::string> output =
67         commonUtility::executeCmd("/sbin/fw_printenv");
68     std::string fitConfigValue;
69 
70     for (const auto& entry : output)
71     {
72         auto pos = entry.find("=");
73         auto key = entry.substr(0, pos);
74         if (key != "fitconfig")
75         {
76             continue;
77         }
78 
79         if (pos + 1 < entry.size())
80         {
81             fitConfigValue = entry.substr(pos + 1);
82         }
83     }
84 
85     return fitConfigValue;
86 }
87 
88 bool Worker::isSystemVPDOnDBus() const
89 {
90     const std::string& mboardPath =
91         m_parsedJson["frus"][SYSTEM_VPD_FILE_PATH].at(0).value(
92             "inventoryPath", "");
93 
94     if (mboardPath.empty())
95     {
96         throw JsonException("System vpd file path missing in JSON",
97                             INVENTORY_JSON_SYM_LINK);
98     }
99 
100     std::array<const char*, 1> interfaces = {
101         "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
102 
103     const types::MapperGetObject& objectMap =
104         dbusUtility::getObjectMap(mboardPath, interfaces);
105 
106     if (objectMap.empty())
107     {
108         return false;
109     }
110     return true;
111 }
112 
113 std::string Worker::getIMValue(const types::IPZVpdMap& parsedVpd) const
114 {
115     if (parsedVpd.empty())
116     {
117         throw std::runtime_error("Empty VPD map. Can't Extract IM value");
118     }
119 
120     const auto& itrToVSBP = parsedVpd.find("VSBP");
121     if (itrToVSBP == parsedVpd.end())
122     {
123         throw DataException("VSBP record missing.");
124     }
125 
126     const auto& itrToIM = (itrToVSBP->second).find("IM");
127     if (itrToIM == (itrToVSBP->second).end())
128     {
129         throw DataException("IM keyword missing.");
130     }
131 
132     types::BinaryVector imVal;
133     std::copy(itrToIM->second.begin(), itrToIM->second.end(),
134               back_inserter(imVal));
135 
136     std::ostringstream imData;
137     for (auto& aByte : imVal)
138     {
139         imData << std::setw(2) << std::setfill('0') << std::hex
140                << static_cast<int>(aByte);
141     }
142 
143     return imData.str();
144 }
145 
146 std::string Worker::getHWVersion(const types::IPZVpdMap& parsedVpd) const
147 {
148     if (parsedVpd.empty())
149     {
150         throw std::runtime_error("Empty VPD map. Can't Extract HW value");
151     }
152 
153     const auto& itrToVINI = parsedVpd.find("VINI");
154     if (itrToVINI == parsedVpd.end())
155     {
156         throw DataException("VINI record missing.");
157     }
158 
159     const auto& itrToHW = (itrToVINI->second).find("HW");
160     if (itrToHW == (itrToVINI->second).end())
161     {
162         throw DataException("HW keyword missing.");
163     }
164 
165     types::BinaryVector hwVal;
166     std::copy(itrToHW->second.begin(), itrToHW->second.end(),
167               back_inserter(hwVal));
168 
169     // The planar pass only comes from the LSB of the HW keyword,
170     // where as the MSB is used for other purposes such as signifying clock
171     // termination.
172     hwVal[0] = 0x00;
173 
174     std::ostringstream hwString;
175     for (auto& aByte : hwVal)
176     {
177         hwString << std::setw(2) << std::setfill('0') << std::hex
178                  << static_cast<int>(aByte);
179     }
180 
181     return hwString.str();
182 }
183 
184 void Worker::fillVPDMap(const std::string& vpdFilePath,
185                         types::VPDMapVariant& vpdMap)
186 {
187     if (vpdFilePath.empty())
188     {
189         throw std::runtime_error("Invalid file path passed to fillVPDMap API.");
190     }
191 
192     if (!std::filesystem::exists(vpdFilePath))
193     {
194         throw std::runtime_error("Can't Find physical file");
195     }
196 
197     std::shared_ptr<Parser> vpdParser =
198         std::make_shared<Parser>(vpdFilePath, m_parsedJson);
199     vpdMap = vpdParser->parse();
200 }
201 
202 void Worker::getSystemJson(std::string& systemJson,
203                            const types::VPDMapVariant& parsedVpdMap)
204 {
205     if (auto pVal = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
206     {
207         std::string hwKWdValue = getHWVersion(*pVal);
208         if (hwKWdValue.empty())
209         {
210             throw DataException("HW value fetched is empty.");
211         }
212 
213         const std::string& imKwdValue = getIMValue(*pVal);
214         if (imKwdValue.empty())
215         {
216             throw DataException("IM value fetched is empty.");
217         }
218 
219         auto itrToIM = config::systemType.find(imKwdValue);
220         if (itrToIM == config::systemType.end())
221         {
222             throw DataException("IM keyword does not map to any system type");
223         }
224 
225         const types::HWVerList hwVersionList = itrToIM->second.second;
226         if (!hwVersionList.empty())
227         {
228             transform(hwKWdValue.begin(), hwKWdValue.end(), hwKWdValue.begin(),
229                       ::toupper);
230 
231             auto itrToHW =
232                 std::find_if(hwVersionList.begin(), hwVersionList.end(),
233                              [&hwKWdValue](const auto& aPair) {
234                                  return aPair.first == hwKWdValue;
235                              });
236 
237             if (itrToHW != hwVersionList.end())
238             {
239                 if (!(*itrToHW).second.empty())
240                 {
241                     systemJson += (*itrToIM).first + "_" + (*itrToHW).second +
242                                   ".json";
243                 }
244                 else
245                 {
246                     systemJson += (*itrToIM).first + ".json";
247                 }
248                 return;
249             }
250         }
251         systemJson += itrToIM->second.first + ".json";
252         return;
253     }
254 
255     throw DataException(
256         "Invalid VPD type returned from Parser. Can't get system JSON.");
257 }
258 
259 static void setEnvAndReboot(const std::string& key, const std::string& value)
260 {
261     // set env and reboot and break.
262     commonUtility::executeCmd("/sbin/fw_setenv", key, value);
263     logging::logMessage("Rebooting BMC to pick up new device tree");
264 
265     // make dbus call to reboot
266     auto bus = sdbusplus::bus::new_default_system();
267     auto method = bus.new_method_call(
268         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
269         "org.freedesktop.systemd1.Manager", "Reboot");
270     bus.call_noreply(method);
271 }
272 
273 void Worker::setJsonSymbolicLink(const std::string& i_systemJson)
274 {
275     std::error_code l_ec;
276     l_ec.clear();
277 
278     // Check if symlink file path exists and if the JSON at this location is a
279     // symlink.
280     if (m_isSymlinkPresent &&
281         std::filesystem::is_symlink(INVENTORY_JSON_SYM_LINK, l_ec))
282     { // Don't care about exception in "is_symlink". Will continue with creation
283       // of symlink.
284 
285         const auto& l_symlinkFilePth =
286             std::filesystem::read_symlink(INVENTORY_JSON_SYM_LINK, l_ec);
287 
288         if (l_ec)
289         {
290             logging::logMessage(
291                 "Can't read existing symlink. Error =" + l_ec.message() +
292                 "Trying removal of symlink and creation of new symlink.");
293         }
294 
295         // If currently set JSON is the required one. No further processing
296         // required.
297         if (i_systemJson == l_symlinkFilePth)
298         {
299             // Correct symlink already set.
300             return;
301         }
302 
303         if (!std::filesystem::remove(INVENTORY_JSON_SYM_LINK, l_ec))
304         {
305             // No point going further. If removal fails for existing symlink,
306             // create will anyways throw.
307             throw std::runtime_error(
308                 "Removal of symlink failed with Error = " + l_ec.message() +
309                 ". Can't proceed with create_symlink.");
310         }
311     }
312 
313     if (!std::filesystem::exists(VPD_SYMLIMK_PATH, l_ec))
314     {
315         if (l_ec)
316         {
317             throw std::runtime_error(
318                 "File system call to exist failed with error = " +
319                 l_ec.message());
320         }
321 
322         // implies it is a fresh boot/factory reset.
323         // Create the directory for hosting the symlink
324         if (!std::filesystem::create_directories(VPD_SYMLIMK_PATH, l_ec))
325         {
326             if (l_ec)
327             {
328                 throw std::runtime_error(
329                     "File system call to create directory failed with error = " +
330                     l_ec.message());
331             }
332         }
333     }
334 
335     // create a new symlink based on the system
336     std::filesystem::create_symlink(i_systemJson, INVENTORY_JSON_SYM_LINK,
337                                     l_ec);
338 
339     if (l_ec)
340     {
341         throw std::runtime_error(
342             "create_symlink system call failed with error: " + l_ec.message());
343     }
344 
345     // If the flow is at this point implies the symlink was not present there.
346     // Considering this as factory reset.
347     m_isFactoryResetDone = true;
348 }
349 
350 void Worker::setDeviceTreeAndJson()
351 {
352     // JSON is madatory for processing of this API.
353     if (m_parsedJson.empty())
354     {
355         throw JsonException("System config JSON is empty", m_configJsonPath);
356     }
357 
358     types::VPDMapVariant parsedVpdMap;
359     fillVPDMap(SYSTEM_VPD_FILE_PATH, parsedVpdMap);
360 
361     // Implies it is default JSON.
362     std::string systemJson{JSON_ABSOLUTE_PATH_PREFIX};
363 
364     // ToDo: Need to check if INVENTORY_JSON_SYM_LINK pointing to correct system
365     // This is required to support movement from rainier to Blue Ridge on the
366     // fly.
367 
368     getSystemJson(systemJson, parsedVpdMap);
369 
370     if (!systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX))
371     {
372         throw DataException(
373             "No system JSON found corresponding to IM read from VPD.");
374     }
375 
376     // re-parse the JSON once appropriate JSON has been selected.
377     m_parsedJson = jsonUtility::getParsedJson(systemJson);
378 
379     if (m_parsedJson.empty())
380     {
381         throw(JsonException("Json parsing failed", systemJson));
382     }
383 
384     std::string devTreeFromJson;
385     if (m_parsedJson.contains("devTree"))
386     {
387         devTreeFromJson = m_parsedJson["devTree"];
388 
389         if (devTreeFromJson.empty())
390         {
391             EventLogger::createSyncPel(
392                 types::ErrorType::JsonFailure, types::SeverityType::Error,
393                 __FILE__, __FUNCTION__, 0,
394                 "Mandatory value for device tree missing from JSON[" +
395                     systemJson + "]",
396                 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
397         }
398     }
399 
400     auto fitConfigVal = readFitConfigValue();
401 
402     if (devTreeFromJson.empty() ||
403         fitConfigVal.find(devTreeFromJson) != std::string::npos)
404     { // Skipping setting device tree as either devtree info is missing from
405       // Json or it is rightly set.
406 
407         setJsonSymbolicLink(systemJson);
408 
409         if (isSystemVPDOnDBus() &&
410             jsonUtility::isBackupAndRestoreRequired(m_parsedJson))
411         {
412             performBackupAndRestore(parsedVpdMap);
413         }
414 
415         // proceed to publish system VPD.
416         publishSystemVPD(parsedVpdMap);
417         return;
418     }
419 
420     setEnvAndReboot("fitconfig", devTreeFromJson);
421     exit(EXIT_SUCCESS);
422 }
423 
424 void Worker::populateIPZVPDpropertyMap(
425     types::InterfaceMap& interfacePropMap,
426     const types::IPZKwdValueMap& keyordValueMap,
427     const std::string& interfaceName)
428 {
429     types::PropertyMap propertyValueMap;
430     for (const auto& kwdVal : keyordValueMap)
431     {
432         auto kwd = kwdVal.first;
433 
434         if (kwd[0] == '#')
435         {
436             kwd = std::string("PD_") + kwd[1];
437         }
438         else if (isdigit(kwd[0]))
439         {
440             kwd = std::string("N_") + kwd;
441         }
442 
443         types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end());
444         propertyValueMap.emplace(move(kwd), move(value));
445     }
446 
447     if (!propertyValueMap.empty())
448     {
449         interfacePropMap.emplace(interfaceName, propertyValueMap);
450     }
451 }
452 
453 void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
454                                        types::InterfaceMap& interfaceMap)
455 {
456     for (const auto& kwdValMap : keyordVPDMap)
457     {
458         types::PropertyMap propertyValueMap;
459         auto kwd = kwdValMap.first;
460 
461         if (kwd[0] == '#')
462         {
463             kwd = std::string("PD_") + kwd[1];
464         }
465         else if (isdigit(kwd[0]))
466         {
467             kwd = std::string("N_") + kwd;
468         }
469 
470         if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second))
471         {
472             types::BinaryVector value((*keywordValue).begin(),
473                                       (*keywordValue).end());
474             propertyValueMap.emplace(move(kwd), move(value));
475         }
476         else if (auto keywordValue = get_if<std::string>(&kwdValMap.second))
477         {
478             types::BinaryVector value((*keywordValue).begin(),
479                                       (*keywordValue).end());
480             propertyValueMap.emplace(move(kwd), move(value));
481         }
482         else if (auto keywordValue = get_if<size_t>(&kwdValMap.second))
483         {
484             if (kwd == "MemorySizeInKB")
485             {
486                 types::PropertyMap memProp;
487                 memProp.emplace(move(kwd), ((*keywordValue)));
488                 interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm",
489                                      move(memProp));
490                 continue;
491             }
492             else
493             {
494                 logging::logMessage(
495                     "Unknown Keyword =" + kwd + " found in keyword VPD map");
496                 continue;
497             }
498         }
499         else
500         {
501             logging::logMessage(
502                 "Unknown variant type found in keyword VPD map.");
503             continue;
504         }
505 
506         if (!propertyValueMap.empty())
507         {
508             vpdSpecificUtility::insertOrMerge(
509                 interfaceMap, constants::kwdVpdInf, move(propertyValueMap));
510         }
511     }
512 }
513 
514 void Worker::populateInterfaces(const nlohmann::json& interfaceJson,
515                                 types::InterfaceMap& interfaceMap,
516                                 const types::VPDMapVariant& parsedVpdMap)
517 {
518     for (const auto& interfacesPropPair : interfaceJson.items())
519     {
520         const std::string& interface = interfacesPropPair.key();
521         types::PropertyMap propertyMap;
522 
523         for (const auto& propValuePair : interfacesPropPair.value().items())
524         {
525             const std::string property = propValuePair.key();
526 
527             if (propValuePair.value().is_boolean())
528             {
529                 propertyMap.emplace(property,
530                                     propValuePair.value().get<bool>());
531             }
532             else if (propValuePair.value().is_string())
533             {
534                 if (property.compare("LocationCode") == 0 &&
535                     interface.compare("com.ibm.ipzvpd.Location") == 0)
536                 {
537                     std::string value =
538                         vpdSpecificUtility::getExpandedLocationCode(
539                             propValuePair.value().get<std::string>(),
540                             parsedVpdMap);
541                     propertyMap.emplace(property, value);
542 
543                     auto l_locCodeProperty = propertyMap;
544                     vpdSpecificUtility::insertOrMerge(
545                         interfaceMap,
546                         std::string(constants::xyzLocationCodeInf),
547                         move(l_locCodeProperty));
548                 }
549                 else
550                 {
551                     propertyMap.emplace(
552                         property, propValuePair.value().get<std::string>());
553                 }
554             }
555             else if (propValuePair.value().is_array())
556             {
557                 try
558                 {
559                     propertyMap.emplace(
560                         property,
561                         propValuePair.value().get<types::BinaryVector>());
562                 }
563                 catch (const nlohmann::detail::type_error& e)
564                 {
565                     std::cerr << "Type exception: " << e.what() << "\n";
566                 }
567             }
568             else if (propValuePair.value().is_number())
569             {
570                 // For now assume the value is a size_t.  In the future it would
571                 // be nice to come up with a way to get the type from the JSON.
572                 propertyMap.emplace(property,
573                                     propValuePair.value().get<size_t>());
574             }
575             else if (propValuePair.value().is_object())
576             {
577                 const std::string& record =
578                     propValuePair.value().value("recordName", "");
579                 const std::string& keyword =
580                     propValuePair.value().value("keywordName", "");
581                 const std::string& encoding =
582                     propValuePair.value().value("encoding", "");
583 
584                 if (auto ipzVpdMap =
585                         std::get_if<types::IPZVpdMap>(&parsedVpdMap))
586                 {
587                     if (!record.empty() && !keyword.empty() &&
588                         (*ipzVpdMap).count(record) &&
589                         (*ipzVpdMap).at(record).count(keyword))
590                     {
591                         auto encoded = vpdSpecificUtility::encodeKeyword(
592                             ((*ipzVpdMap).at(record).at(keyword)), encoding);
593                         propertyMap.emplace(property, encoded);
594                     }
595                 }
596                 else if (auto kwdVpdMap =
597                              std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
598                 {
599                     if (!keyword.empty() && (*kwdVpdMap).count(keyword))
600                     {
601                         if (auto kwValue = std::get_if<types::BinaryVector>(
602                                 &(*kwdVpdMap).at(keyword)))
603                         {
604                             auto encodedValue =
605                                 vpdSpecificUtility::encodeKeyword(
606                                     std::string((*kwValue).begin(),
607                                                 (*kwValue).end()),
608                                     encoding);
609 
610                             propertyMap.emplace(property, encodedValue);
611                         }
612                         else if (auto kwValue = std::get_if<std::string>(
613                                      &(*kwdVpdMap).at(keyword)))
614                         {
615                             auto encodedValue =
616                                 vpdSpecificUtility::encodeKeyword(
617                                     std::string((*kwValue).begin(),
618                                                 (*kwValue).end()),
619                                     encoding);
620 
621                             propertyMap.emplace(property, encodedValue);
622                         }
623                         else if (auto uintValue = std::get_if<size_t>(
624                                      &(*kwdVpdMap).at(keyword)))
625                         {
626                             propertyMap.emplace(property, *uintValue);
627                         }
628                         else
629                         {
630                             logging::logMessage(
631                                 "Unknown keyword found, Keywrod = " + keyword);
632                         }
633                     }
634                 }
635             }
636         }
637         vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
638                                           move(propertyMap));
639     }
640 }
641 
642 bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
643 {
644     const unsigned char l_io[] = {
645         0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
646         0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
647 
648     // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
649     // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
650     // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
651     // IO.
652     if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
653                constants::SIZE_OF_8EQ_IN_PG) == 0)
654     {
655         return true;
656     }
657 
658     // The CPU is not an IO
659     return false;
660 }
661 
662 bool Worker::primeInventory(const std::string& i_vpdFilePath)
663 {
664     if (i_vpdFilePath.empty())
665     {
666         logging::logMessage("Empty VPD file path given");
667         return false;
668     }
669 
670     if (m_parsedJson.empty())
671     {
672         logging::logMessage("Empty JSON detected for " + i_vpdFilePath);
673         return false;
674     }
675     else if (!m_parsedJson["frus"].contains(i_vpdFilePath))
676     {
677         logging::logMessage("File " + i_vpdFilePath +
678                             ", is not found in the system config JSON file.");
679         return false;
680     }
681 
682     types::ObjectMap l_objectInterfaceMap;
683     for (const auto& l_Fru : m_parsedJson["frus"][i_vpdFilePath])
684     {
685         types::InterfaceMap l_interfaces;
686         sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
687 
688         if (l_Fru.contains("ccin"))
689         {
690             continue;
691         }
692 
693         if (l_Fru.contains("noprime") && l_Fru.value("noprime", false))
694         {
695             continue;
696         }
697 
698         // Reset data under PIM for this FRU only if the FRU is not synthesized
699         // and we handle it's Present property.
700         if (isPresentPropertyHandlingRequired(l_Fru))
701         {
702             // Clear data under PIM if already exists.
703             vpdSpecificUtility::resetDataUnderPIM(
704                 std::string(l_Fru["inventoryPath"]), l_interfaces);
705         }
706 
707         // Add extra interfaces mentioned in the Json config file
708         if (l_Fru.contains("extraInterfaces"))
709         {
710             populateInterfaces(l_Fru["extraInterfaces"], l_interfaces,
711                                std::monostate{});
712         }
713 
714         types::PropertyMap l_propertyValueMap;
715 
716         // Update Present property for this FRU only if we handle Present
717         // property for the FRU.
718         if (isPresentPropertyHandlingRequired(l_Fru))
719         {
720             l_propertyValueMap.emplace("Present", false);
721 
722             // TODO: Present based on file will be taken care in future.
723             // By default present is set to false for FRU at the time of
724             // priming. Once collection goes through, it will be set to true in
725             // that flow.
726             /*if (std::filesystem::exists(i_vpdFilePath))
727             {
728                 l_propertyValueMap["Present"] = true;
729             }*/
730         }
731 
732         vpdSpecificUtility::insertOrMerge(l_interfaces,
733                                           "xyz.openbmc_project.Inventory.Item",
734                                           move(l_propertyValueMap));
735 
736         if (l_Fru.value("inherit", true) &&
737             m_parsedJson.contains("commonInterfaces"))
738         {
739             populateInterfaces(m_parsedJson["commonInterfaces"], l_interfaces,
740                                std::monostate{});
741         }
742 
743         processFunctionalProperty(l_Fru["inventoryPath"], l_interfaces);
744         processEnabledProperty(l_Fru["inventoryPath"], l_interfaces);
745 
746         // Emplace the default state of FRU VPD collection
747         types::PropertyMap l_fruCollectionProperty = {
748             {"Status", constants::vpdCollectionNotStarted}};
749 
750         vpdSpecificUtility::insertOrMerge(l_interfaces,
751                                           constants::vpdCollectionInterface,
752                                           std::move(l_fruCollectionProperty));
753 
754         l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
755                                      std::move(l_interfaces));
756     }
757 
758     // Notify PIM
759     if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
760     {
761         logging::logMessage("Call to PIM failed for VPD file " + i_vpdFilePath);
762         return false;
763     }
764 
765     return true;
766 }
767 
768 void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
769                                                types::InterfaceMap& interfaces)
770 {
771     // embedded property(true or false) says whether the subfru is embedded
772     // into the parent fru (or) not. VPD sets Present property only for
773     // embedded frus. If the subfru is not an embedded FRU, the subfru may
774     // or may not be physically present. Those non embedded frus will always
775     // have Present=false irrespective of its physical presence or absence.
776     // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
777     // Present to true for such sub frus.
778     // Eg: ethernet port is embedded into bmc card. So set Present to true
779     // for such sub frus. Also donot populate present property for embedded
780     // subfru which is synthesized. Currently there is no subfru which are
781     // both embedded and synthesized. But still the case is handled here.
782 
783     // Check if its required to handle presence for this FRU.
784     if (singleFru.value("handlePresence", true))
785     {
786         types::PropertyMap presProp;
787         presProp.emplace("Present", true);
788         vpdSpecificUtility::insertOrMerge(
789             interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp));
790     }
791 }
792 
793 void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
794                                     types::InterfaceMap& interfaces,
795                                     const types::VPDMapVariant& parsedVpdMap)
796 {
797     populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
798     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
799     {
800         if (singleFru["extraInterfaces"].contains(
801                 "xyz.openbmc_project.Inventory.Item.Cpu"))
802         {
803             auto itrToRec = (*ipzVpdMap).find("CP00");
804             if (itrToRec == (*ipzVpdMap).end())
805             {
806                 return;
807             }
808 
809             const std::string pgKeywordValue{
810                 vpdSpecificUtility::getKwVal(itrToRec->second, "PG")};
811 
812             if (!pgKeywordValue.empty())
813             {
814                 if (isCPUIOGoodOnly(pgKeywordValue))
815                 {
816                     interfaces["xyz.openbmc_project.Inventory.Item"]
817                               ["PrettyName"] = "IO Module";
818                 }
819             }
820             else
821             {
822                 throw DataException(std::string(__FUNCTION__) +
823                                     "Failed to get value for keyword PG");
824             }
825         }
826     }
827 }
828 
829 void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
830                                    const types::VPDMapVariant& parsedVpdMap,
831                                    types::InterfaceMap& interfaces)
832 {
833     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
834     {
835         for (const auto& record : singleFru["copyRecords"])
836         {
837             const std::string& recordName = record;
838             if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
839             {
840                 populateIPZVPDpropertyMap(interfaces,
841                                           (*ipzVpdMap).at(recordName),
842                                           constants::ipzVpdInf + recordName);
843             }
844         }
845     }
846 }
847 
848 void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
849                                 types::InterfaceMap& interfaces)
850 {
851     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
852     {
853         for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
854         {
855             populateIPZVPDpropertyMap(interfaces, kwdValueMap,
856                                       constants::ipzVpdInf + recordName);
857         }
858     }
859     else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
860     {
861         populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
862     }
863 
864     if (m_parsedJson.contains("commonInterfaces"))
865     {
866         populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
867                            parsedVpdMap);
868     }
869 }
870 
871 bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
872                                 const types::VPDMapVariant& parsedVpdMap)
873 {
874     if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
875     {
876         auto itrToRec = (*ipzVPDMap).find("VINI");
877         if (itrToRec == (*ipzVPDMap).end())
878         {
879             return false;
880         }
881 
882         std::string ccinFromVpd{
883             vpdSpecificUtility::getKwVal(itrToRec->second, "CC")};
884 
885         if (ccinFromVpd.empty())
886         {
887             return false;
888         }
889 
890         transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
891                   ::toupper);
892 
893         std::vector<std::string> ccinList;
894         for (std::string ccin : singleFru["ccin"])
895         {
896             transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
897             ccinList.push_back(ccin);
898         }
899 
900         if (ccinList.empty())
901         {
902             return false;
903         }
904 
905         if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
906             ccinList.end())
907         {
908             return false;
909         }
910     }
911     return true;
912 }
913 
914 void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
915                                        types::InterfaceMap& io_interfaces)
916 {
917     if (!dbusUtility::isChassisPowerOn())
918     {
919         std::array<const char*, 1> l_operationalStatusInf = {
920             constants::operationalStatusInf};
921 
922         auto mapperObjectMap = dbusUtility::getObjectMap(
923             i_inventoryObjPath, l_operationalStatusInf);
924 
925         // If the object has been found. Check if it is under PIM.
926         if (mapperObjectMap.size() != 0)
927         {
928             for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
929             {
930                 if (l_serviceName == constants::pimServiceName)
931                 {
932                     // The object is already under PIM. No need to process
933                     // again. Retain the old value.
934                     return;
935                 }
936             }
937         }
938 
939         // Implies value is not there in D-Bus. Populate it with default
940         // value "true".
941         types::PropertyMap l_functionalProp;
942         l_functionalProp.emplace("Functional", true);
943         vpdSpecificUtility::insertOrMerge(io_interfaces,
944                                           constants::operationalStatusInf,
945                                           move(l_functionalProp));
946     }
947 
948     // if chassis is power on. Functional property should be there on D-Bus.
949     // Don't process.
950     return;
951 }
952 
953 void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
954                                     types::InterfaceMap& io_interfaces)
955 {
956     if (!dbusUtility::isChassisPowerOn())
957     {
958         std::array<const char*, 1> l_enableInf = {constants::enableInf};
959 
960         auto mapperObjectMap =
961             dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
962 
963         // If the object has been found. Check if it is under PIM.
964         if (mapperObjectMap.size() != 0)
965         {
966             for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
967             {
968                 if (l_serviceName == constants::pimServiceName)
969                 {
970                     // The object is already under PIM. No need to process
971                     // again. Retain the old value.
972                     return;
973                 }
974             }
975         }
976 
977         // Implies value is not there in D-Bus. Populate it with default
978         // value "true".
979         types::PropertyMap l_enabledProp;
980         l_enabledProp.emplace("Enabled", true);
981         vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
982                                           move(l_enabledProp));
983     }
984 
985     // if chassis is power on. Enabled property should be there on D-Bus.
986     // Don't process.
987     return;
988 }
989 
990 void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
991                           types::ObjectMap& objectInterfaceMap,
992                           const std::string& vpdFilePath)
993 {
994     if (vpdFilePath.empty())
995     {
996         throw std::runtime_error(
997             std::string(__FUNCTION__) +
998             "Invalid parameter passed to populateDbus API.");
999     }
1000 
1001     // JSON config is mandatory for processing of "if". Add "else" for any
1002     // processing without config JSON.
1003     if (!m_parsedJson.empty())
1004     {
1005         types::InterfaceMap interfaces;
1006 
1007         for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
1008         {
1009             const auto& inventoryPath = aFru["inventoryPath"];
1010             sdbusplus::message::object_path fruObjectPath(inventoryPath);
1011             if (aFru.contains("ccin"))
1012             {
1013                 if (!processFruWithCCIN(aFru, parsedVpdMap))
1014                 {
1015                     continue;
1016                 }
1017             }
1018 
1019             if (aFru.value("inherit", true))
1020             {
1021                 processInheritFlag(parsedVpdMap, interfaces);
1022             }
1023 
1024             // If specific record needs to be copied.
1025             if (aFru.contains("copyRecords"))
1026             {
1027                 processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
1028             }
1029 
1030             if (aFru.contains("extraInterfaces"))
1031             {
1032                 // Process extra interfaces w.r.t a FRU.
1033                 processExtraInterfaces(aFru, interfaces, parsedVpdMap);
1034             }
1035 
1036             // Process FRUS which are embedded in the parent FRU and whose VPD
1037             // will be synthesized.
1038             if ((aFru.value("embedded", true)) &&
1039                 (!aFru.value("synthesized", false)))
1040             {
1041                 processEmbeddedAndSynthesizedFrus(aFru, interfaces);
1042             }
1043 
1044             processFunctionalProperty(inventoryPath, interfaces);
1045             processEnabledProperty(inventoryPath, interfaces);
1046 
1047             // Update collection status as successful
1048             types::PropertyMap l_collectionProperty = {
1049                 {"Status", constants::vpdCollectionCompleted}};
1050 
1051             vpdSpecificUtility::insertOrMerge(interfaces,
1052                                               constants::vpdCollectionInterface,
1053                                               std::move(l_collectionProperty));
1054 
1055             objectInterfaceMap.emplace(std::move(fruObjectPath),
1056                                        std::move(interfaces));
1057         }
1058     }
1059 }
1060 
1061 std::string Worker::createAssetTagString(
1062     const types::VPDMapVariant& i_parsedVpdMap)
1063 {
1064     std::string l_assetTag;
1065 
1066     // system VPD will be in IPZ format.
1067     if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
1068     {
1069         auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
1070         if (l_itrToVsys != (*l_parsedVpdMap).end())
1071         {
1072             const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal(
1073                 l_itrToVsys->second, constants::kwdTM)};
1074 
1075             if (l_tmKwdValue.empty())
1076             {
1077                 throw std::runtime_error(
1078                     std::string("Failed to get value for keyword [") +
1079                     constants::kwdTM +
1080                     std::string("] while creating Asset tag."));
1081             }
1082 
1083             const std::string l_seKwdValue{vpdSpecificUtility::getKwVal(
1084                 l_itrToVsys->second, constants::kwdSE)};
1085 
1086             if (l_seKwdValue.empty())
1087             {
1088                 throw std::runtime_error(
1089                     std::string("Failed to get value for keyword [") +
1090                     constants::kwdSE +
1091                     std::string("] while creating Asset tag."));
1092             }
1093 
1094             l_assetTag = std::string{"Server-"} + l_tmKwdValue +
1095                          std::string{"-"} + l_seKwdValue;
1096         }
1097         else
1098         {
1099             throw std::runtime_error(
1100                 "VSYS record not found in parsed VPD map to create Asset tag.");
1101         }
1102     }
1103     else
1104     {
1105         throw std::runtime_error(
1106             "Invalid VPD type recieved to create Asset tag.");
1107     }
1108 
1109     return l_assetTag;
1110 }
1111 
1112 void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
1113 {
1114     types::ObjectMap objectInterfaceMap;
1115 
1116     if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
1117     {
1118         populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
1119 
1120         try
1121         {
1122             if (m_isFactoryResetDone)
1123             {
1124                 const auto& l_assetTag = createAssetTagString(parsedVpdMap);
1125 
1126                 auto l_itrToSystemPath = objectInterfaceMap.find(
1127                     sdbusplus::message::object_path(constants::systemInvPath));
1128                 if (l_itrToSystemPath == objectInterfaceMap.end())
1129                 {
1130                     throw std::runtime_error(
1131                         "Asset tag update failed. System Path not found in object map.");
1132                 }
1133 
1134                 types::PropertyMap l_assetTagProperty;
1135                 l_assetTagProperty.emplace("AssetTag", l_assetTag);
1136 
1137                 (l_itrToSystemPath->second)
1138                     .emplace(constants::assetTagInf,
1139                              std::move(l_assetTagProperty));
1140             }
1141         }
1142         catch (const std::exception& l_ex)
1143         {
1144             EventLogger::createSyncPel(
1145                 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1146                 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1147                 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1148         }
1149 
1150         // Notify PIM
1151         if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1152         {
1153             throw std::runtime_error("Call to PIM failed for system VPD");
1154         }
1155     }
1156     else
1157     {
1158         throw DataException("Invalid format of parsed VPD map.");
1159     }
1160 }
1161 
1162 bool Worker::processPreAction(const std::string& i_vpdFilePath,
1163                               const std::string& i_flagToProcess)
1164 {
1165     if (i_vpdFilePath.empty() || i_flagToProcess.empty())
1166     {
1167         logging::logMessage(
1168             "Invalid input parameter. Abort processing pre action");
1169         return false;
1170     }
1171 
1172     if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
1173                                          i_vpdFilePath, i_flagToProcess)) &&
1174         (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
1175     {
1176         // TODO: Need a way to delete inventory object from Dbus and persisted
1177         // data section in case any FRU is not present or there is any
1178         // problem in collecting it. Once it has been deleted, it can be
1179         // re-created in the flow of priming the inventory. This needs to be
1180         // done either here or in the exception section of "parseAndPublishVPD"
1181         // API. Any failure in the process of collecting FRU will land up in the
1182         // excpetion of "parseAndPublishVPD".
1183 
1184         // If the FRU is not there, clear the VINI/CCIN data.
1185         // Enity manager probes for this keyword to look for this
1186         // FRU, now if the data is persistent on BMC and FRU is
1187         // removed this can lead to ambiguity. Hence clearing this
1188         // Keyword if FRU is absent.
1189         const auto& inventoryPath =
1190             m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
1191                                                             "");
1192 
1193         if (!inventoryPath.empty())
1194         {
1195             types::ObjectMap l_pimObjMap{
1196                 {inventoryPath,
1197                  {{constants::kwdVpdInf,
1198                    {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
1199 
1200             if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
1201             {
1202                 logging::logMessage(
1203                     "Call to PIM failed for file " + i_vpdFilePath);
1204             }
1205         }
1206         else
1207         {
1208             logging::logMessage(
1209                 "Inventory path is empty in Json for file " + i_vpdFilePath);
1210         }
1211 
1212         return false;
1213     }
1214     return true;
1215 }
1216 
1217 bool Worker::processPostAction(
1218     const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
1219     const std::optional<types::VPDMapVariant> i_parsedVpd)
1220 {
1221     if (i_vpdFruPath.empty() || i_flagToProcess.empty())
1222     {
1223         logging::logMessage(
1224             "Invalid input parameter. Abort processing post action");
1225         return false;
1226     }
1227 
1228     // Check if post action tag is to be triggered in the flow of collection
1229     // based on some CCIN value?
1230     if (m_parsedJson["frus"][i_vpdFruPath]
1231             .at(0)["postAction"][i_flagToProcess]
1232             .contains("ccin"))
1233     {
1234         if (!i_parsedVpd.has_value())
1235         {
1236             logging::logMessage("Empty VPD Map");
1237             return false;
1238         }
1239 
1240         // CCIN match is required to process post action for this FRU as it
1241         // contains the flag.
1242         if (!vpdSpecificUtility::findCcinInVpd(
1243                 m_parsedJson["frus"][i_vpdFruPath].at(
1244                     0)["postAction"]["collection"],
1245                 i_parsedVpd.value()))
1246         {
1247             // If CCIN is not found, implies post action processing is not
1248             // required for this FRU. Let the flow continue.
1249             return true;
1250         }
1251     }
1252 
1253     if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
1254                                         i_vpdFruPath, i_flagToProcess))
1255     {
1256         logging::logMessage(
1257             "Execution of post action failed for path: " + i_vpdFruPath);
1258 
1259         // If post action was required and failed only in that case return
1260         // false. In all other case post action is considered passed.
1261         return false;
1262     }
1263 
1264     return true;
1265 }
1266 
1267 types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
1268 {
1269     try
1270     {
1271         if (i_vpdFilePath.empty())
1272         {
1273             throw std::runtime_error(
1274                 std::string(__FUNCTION__) +
1275                 " Empty VPD file path passed. Abort processing");
1276         }
1277 
1278         bool isPreActionRequired = false;
1279         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1280                                           "preAction", "collection"))
1281         {
1282             isPreActionRequired = true;
1283             if (!processPreAction(i_vpdFilePath, "collection"))
1284             {
1285                 throw std::runtime_error(
1286                     std::string(__FUNCTION__) + " Pre-Action failed");
1287             }
1288         }
1289 
1290         if (!std::filesystem::exists(i_vpdFilePath))
1291         {
1292             if (isPreActionRequired)
1293             {
1294                 throw std::runtime_error(
1295                     std::string(__FUNCTION__) + " Could not find file path " +
1296                     i_vpdFilePath + "Skipping parser trigger for the EEPROM");
1297             }
1298             return types::VPDMapVariant{};
1299         }
1300 
1301         std::shared_ptr<Parser> vpdParser =
1302             std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
1303 
1304         types::VPDMapVariant l_parsedVpd = vpdParser->parse();
1305 
1306         // Before returning, as collection is over, check if FRU qualifies for
1307         // any post action in the flow of collection.
1308         // Note: Don't change the order, post action needs to be processed only
1309         // after collection for FRU is successfully done.
1310         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1311                                           "postAction", "collection"))
1312         {
1313             if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
1314             {
1315                 // Post action was required but failed while executing.
1316                 // Behaviour can be undefined.
1317                 EventLogger::createSyncPel(
1318                     types::ErrorType::InternalFailure,
1319                     types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
1320                     std::string("Required post action failed for path [" +
1321                                 i_vpdFilePath + "]"),
1322                     std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1323             }
1324         }
1325 
1326         return l_parsedVpd;
1327     }
1328     catch (std::exception& l_ex)
1329     {
1330         std::string l_exMsg{
1331             std::string(__FUNCTION__) + " : VPD parsing failed for " +
1332             i_vpdFilePath + " due to error: " + l_ex.what()};
1333 
1334         // If post fail action is required, execute it.
1335         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1336                                           "postFailAction", "collection"))
1337         {
1338             if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
1339                                                     "collection"))
1340             {
1341                 l_exMsg +=
1342                     ". Post Fail Action also failed, aborting collection for this FRU";
1343             }
1344         }
1345 
1346         if (typeid(l_ex) == typeid(DataException))
1347         {
1348             throw DataException(l_exMsg);
1349         }
1350         else if (typeid(l_ex) == typeid(EccException))
1351         {
1352             throw EccException(l_exMsg);
1353         }
1354         throw std::runtime_error(l_exMsg);
1355     }
1356 }
1357 
1358 std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1359     const std::string& i_vpdFilePath)
1360 {
1361     std::string l_inventoryPath{};
1362 
1363     try
1364     {
1365         m_semaphore.acquire();
1366 
1367         // Thread launched.
1368         m_mutex.lock();
1369         m_activeCollectionThreadCount++;
1370         m_mutex.unlock();
1371 
1372         // Set collection Status as InProgress. Since it's an intermediate state
1373         // D-bus set-property call is good enough to update the status.
1374         l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
1375             m_parsedJson, i_vpdFilePath);
1376 
1377         if (!l_inventoryPath.empty())
1378         {
1379             if (!dbusUtility::writeDbusProperty(
1380                     jsonUtility::getServiceName(m_parsedJson, l_inventoryPath),
1381                     l_inventoryPath, constants::vpdCollectionInterface,
1382                     "Status",
1383                     types::DbusVariantType{constants::vpdCollectionInProgress}))
1384             {
1385                 logging::logMessage(
1386                     "Unable to set collection Status as InProgress for " +
1387                     i_vpdFilePath + ". Error : " + "DBus write failed");
1388             }
1389         }
1390 
1391         const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
1392         if (!std::holds_alternative<std::monostate>(parsedVpdMap))
1393         {
1394             types::ObjectMap objectInterfaceMap;
1395             populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1396 
1397             // Notify PIM
1398             if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1399             {
1400                 throw std::runtime_error(
1401                     std::string(__FUNCTION__) +
1402                     "Call to PIM failed while publishing VPD.");
1403             }
1404         }
1405         else
1406         {
1407             logging::logMessage("Empty parsedVpdMap recieved for path [" +
1408                                 i_vpdFilePath + "]. Check PEL for reason.");
1409         }
1410     }
1411     catch (const std::exception& ex)
1412     {
1413         setCollectionStatusProperty(i_vpdFilePath,
1414                                     constants::vpdCollectionFailed);
1415 
1416         // handle all the exceptions internally. Return only true/false
1417         // based on status of execution.
1418         if (typeid(ex) == std::type_index(typeid(DataException)))
1419         {
1420             // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1421             // logging error for these cases.
1422             if (vpdSpecificUtility::isPass1Planar())
1423             {
1424                 const std::string& l_invPathLeafValue =
1425                     sdbusplus::message::object_path(
1426                         jsonUtility::getInventoryObjPathFromJson(m_parsedJson,
1427                                                                  i_vpdFilePath))
1428                         .filename();
1429 
1430                 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1431                      std::string::npos))
1432                 {
1433                     // skip logging any PEL for PCIe cards on pass 1 planar.
1434                     return std::make_tuple(false, i_vpdFilePath);
1435                 }
1436             }
1437         }
1438 
1439         EventLogger::createSyncPel(
1440             EventLogger::getErrorType(ex),
1441             (typeid(ex) == typeid(DataException)) ||
1442                     (typeid(ex) == typeid(EccException))
1443                 ? types::SeverityType::Warning
1444                 : types::SeverityType::Informational,
1445             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1446             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1447 
1448         // TODO: Figure out a way to clear data in case of any failure at
1449         // runtime.
1450 
1451         // set present property to false for any error case. In future this will
1452         // be replaced by presence logic.
1453         // Update Present property for this FRU only if we handle Present
1454         // property for the FRU.
1455         if (isPresentPropertyHandlingRequired(
1456                 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1457         {
1458             setPresentProperty(i_vpdFilePath, false);
1459         }
1460 
1461         m_semaphore.release();
1462         return std::make_tuple(false, i_vpdFilePath);
1463     }
1464     m_semaphore.release();
1465     return std::make_tuple(true, i_vpdFilePath);
1466 }
1467 
1468 bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1469 {
1470     if (i_vpdFilePath.empty())
1471     {
1472         return true;
1473     }
1474 
1475     // skip processing of system VPD again as it has been already collected.
1476     if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1477     {
1478         return true;
1479     }
1480 
1481     if (dbusUtility::isChassisPowerOn())
1482     {
1483         // If chassis is powered on, skip collecting FRUs which are
1484         // powerOffOnly.
1485         if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath))
1486         {
1487             return true;
1488         }
1489 
1490         const std::string& l_invPathLeafValue =
1491             sdbusplus::message::object_path(
1492                 jsonUtility::getInventoryObjPathFromJson(m_parsedJson,
1493                                                          i_vpdFilePath))
1494                 .filename();
1495 
1496         if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1497         {
1498             return true;
1499         }
1500     }
1501 
1502     return false;
1503 }
1504 
1505 void Worker::collectFrusFromJson()
1506 {
1507     // A parsed JSON file should be present to pick FRUs EEPROM paths
1508     if (m_parsedJson.empty())
1509     {
1510         throw JsonException(
1511             std::string(__FUNCTION__) +
1512                 ": Config JSON is mandatory for processing of FRUs through this API.",
1513             m_configJsonPath);
1514     }
1515 
1516     const nlohmann::json& listOfFrus =
1517         m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1518 
1519     for (const auto& itemFRUS : listOfFrus.items())
1520     {
1521         const std::string& vpdFilePath = itemFRUS.key();
1522 
1523         if (skipPathForCollection(vpdFilePath))
1524         {
1525             continue;
1526         }
1527 
1528         try
1529         {
1530             std::thread{[vpdFilePath, this]() {
1531                 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
1532 
1533                 m_mutex.lock();
1534                 m_activeCollectionThreadCount--;
1535                 m_mutex.unlock();
1536 
1537                 if (!m_activeCollectionThreadCount)
1538                 {
1539                     m_isAllFruCollected = true;
1540                 }
1541             }}.detach();
1542         }
1543         catch (const std::exception& l_ex)
1544         {
1545             // add vpdFilePath(EEPROM path) to failed list
1546             m_failedEepromPaths.push_front(vpdFilePath);
1547         }
1548     }
1549 }
1550 
1551 // ToDo: Move the API under IBM_SYSTEM
1552 void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1553 {
1554     try
1555     {
1556         std::string l_backupAndRestoreCfgFilePath =
1557             m_parsedJson.value("backupRestoreConfigPath", "");
1558 
1559         nlohmann::json l_backupAndRestoreCfgJsonObj =
1560             jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath);
1561 
1562         if (l_backupAndRestoreCfgJsonObj.empty())
1563         {
1564             throw JsonException("JSON parsing failed",
1565                                 l_backupAndRestoreCfgFilePath);
1566         }
1567 
1568         // check if either of "source" or "destination" has inventory path.
1569         // this indicates that this sytem has System VPD on hardware
1570         // and other copy on D-Bus (BMC cache).
1571         if (!l_backupAndRestoreCfgJsonObj.empty() &&
1572             ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1573               l_backupAndRestoreCfgJsonObj["source"].contains(
1574                   "inventoryPath")) ||
1575              (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1576               l_backupAndRestoreCfgJsonObj["destination"].contains(
1577                   "inventoryPath"))))
1578         {
1579             BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1580             auto [l_srcVpdVariant,
1581                   l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1582 
1583             // ToDo: Revisit is this check is required or not.
1584             if (auto l_srcVpdMap =
1585                     std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1586                 l_srcVpdMap && !(*l_srcVpdMap).empty())
1587             {
1588                 io_srcVpdMap = std::move(l_srcVpdVariant);
1589             }
1590         }
1591     }
1592     catch (const std::exception& l_ex)
1593     {
1594         EventLogger::createSyncPel(
1595             EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1596             __FILE__, __FUNCTION__, 0,
1597             std::string(
1598                 "Exception caught while backup and restore VPD keyword's.") +
1599                 EventLogger::getErrorMsg(l_ex),
1600             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1601     }
1602 }
1603 
1604 void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1605 {
1606     if (i_dbusObjPath.empty())
1607     {
1608         throw std::runtime_error("Given DBus object path is empty.");
1609     }
1610 
1611     const std::string& l_fruPath =
1612         jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1613 
1614     try
1615     {
1616         auto l_presentPropValue = dbusUtility::readDbusProperty(
1617             constants::pimServiceName, i_dbusObjPath,
1618             constants::inventoryItemInf, "Present");
1619 
1620         if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1621         {
1622             // check if FRU's Present property is handled by vpd-manager
1623             const auto& l_isFruPresenceHandled =
1624                 jsonUtility::isFruPresenceHandled(m_parsedJson, l_fruPath);
1625 
1626             if (!(*l_value) && l_isFruPresenceHandled)
1627             {
1628                 throw std::runtime_error("Given FRU is not present");
1629             }
1630             else if (*l_value && !l_isFruPresenceHandled)
1631             {
1632                 throw std::runtime_error(
1633                     "Given FRU is present and its presence is not handled by vpd-manager.");
1634             }
1635             else
1636             {
1637                 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1638                                                   "preAction", "deletion"))
1639                 {
1640                     if (!processPreAction(l_fruPath, "deletion"))
1641                     {
1642                         throw std::runtime_error("Pre action failed");
1643                     }
1644                 }
1645 
1646                 std::vector<std::string> l_interfaceList{
1647                     constants::operationalStatusInf};
1648 
1649                 types::MapperGetSubTree l_subTreeMap =
1650                     dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1651                                                   l_interfaceList);
1652 
1653                 types::ObjectMap l_objectMap;
1654 
1655                 // Updates VPD specific interfaces property value under PIM for
1656                 // sub FRUs.
1657                 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1658                      l_subTreeMap)
1659                 {
1660                     types::InterfaceMap l_interfaceMap;
1661                     vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1662                                                           l_interfaceMap);
1663                     l_objectMap.emplace(l_objectPath,
1664                                         std::move(l_interfaceMap));
1665                 }
1666 
1667                 types::InterfaceMap l_interfaceMap;
1668                 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1669                                                       l_interfaceMap);
1670 
1671                 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1672 
1673                 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1674                 {
1675                     throw std::runtime_error("Call to PIM failed.");
1676                 }
1677 
1678                 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1679                                                   "postAction", "deletion"))
1680                 {
1681                     if (!processPostAction(l_fruPath, "deletion"))
1682                     {
1683                         throw std::runtime_error("Post action failed");
1684                     }
1685                 }
1686             }
1687         }
1688         else
1689         {
1690             logging::logMessage(
1691                 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1692                 "] as unable to read present property");
1693             return;
1694         }
1695 
1696         logging::logMessage(
1697             "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1698     }
1699     catch (const std::exception& l_ex)
1700     {
1701         if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1702                                           "postFailAction", "deletion"))
1703         {
1704             if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
1705                                                     "deletion"))
1706             {
1707                 logging::logMessage(
1708                     "Post fail action failed for: " + i_dbusObjPath);
1709             }
1710         }
1711 
1712         logging::logMessage("Failed to delete VPD for FRU : " + i_dbusObjPath +
1713                             " error: " + std::string(l_ex.what()));
1714     }
1715 }
1716 
1717 void Worker::setPresentProperty(const std::string& i_vpdPath,
1718                                 const bool& i_value)
1719 {
1720     try
1721     {
1722         if (i_vpdPath.empty())
1723         {
1724             throw std::runtime_error(
1725                 "Path is empty. Can't set present property");
1726         }
1727 
1728         types::ObjectMap l_objectInterfaceMap;
1729 
1730         // If the given path is EEPROM path.
1731         if (m_parsedJson["frus"].contains(i_vpdPath))
1732         {
1733             for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1734             {
1735                 sdbusplus::message::object_path l_fruObjectPath(
1736                     l_Fru["inventoryPath"]);
1737 
1738                 types::PropertyMap l_propertyValueMap;
1739                 l_propertyValueMap.emplace("Present", i_value);
1740 
1741                 types::InterfaceMap l_interfaces;
1742                 vpdSpecificUtility::insertOrMerge(l_interfaces,
1743                                                   constants::inventoryItemInf,
1744                                                   move(l_propertyValueMap));
1745 
1746                 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1747                                              std::move(l_interfaces));
1748             }
1749         }
1750         else
1751         {
1752             // consider it as an inventory path.
1753             if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1754             {
1755                 throw std::runtime_error(
1756                     "Invalid inventory path: " + i_vpdPath);
1757             }
1758 
1759             types::PropertyMap l_propertyValueMap;
1760             l_propertyValueMap.emplace("Present", i_value);
1761 
1762             types::InterfaceMap l_interfaces;
1763             vpdSpecificUtility::insertOrMerge(l_interfaces,
1764                                               constants::inventoryItemInf,
1765                                               move(l_propertyValueMap));
1766 
1767             l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1768         }
1769 
1770         // Notify PIM
1771         if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1772         {
1773             throw DbusException(
1774                 std::string(__FUNCTION__) +
1775                 "Call to PIM failed while setting present property for path " +
1776                 i_vpdPath);
1777         }
1778     }
1779     catch (const std::exception& l_ex)
1780     {
1781         EventLogger::createSyncPel(
1782             EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1783             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1784             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1785     }
1786 }
1787 
1788 void Worker::performVpdRecollection()
1789 {
1790     try
1791     {
1792         // Check if system config JSON is present
1793         if (m_parsedJson.empty())
1794         {
1795             throw std::runtime_error(
1796                 "System config json object is empty, can't process recollection.");
1797         }
1798 
1799         const auto& l_frusReplaceableAtStandby =
1800             jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson);
1801 
1802         for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1803         {
1804             // ToDo: Add some logic/trace to know the flow to
1805             // collectSingleFruVpd has been directed via
1806             // performVpdRecollection.
1807             collectSingleFruVpd(l_fruInventoryPath);
1808         }
1809         return;
1810     }
1811 
1812     catch (const std::exception& l_ex)
1813     {
1814         // TODO Log PEL
1815         logging::logMessage(
1816             "VPD recollection failed with error: " + std::string(l_ex.what()));
1817     }
1818 }
1819 
1820 void Worker::collectSingleFruVpd(
1821     const sdbusplus::message::object_path& i_dbusObjPath)
1822 {
1823     try
1824     {
1825         // Check if system config JSON is present
1826         if (m_parsedJson.empty())
1827         {
1828             logging::logMessage(
1829                 "System config JSON object not present. Single FRU VPD collection is not performed for " +
1830                 std::string(i_dbusObjPath));
1831             return;
1832         }
1833 
1834         // Get FRU path for the given D-bus object path from JSON
1835         const std::string& l_fruPath =
1836             jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1837 
1838         if (l_fruPath.empty())
1839         {
1840             logging::logMessage(
1841                 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
1842                 std::string(i_dbusObjPath));
1843             return;
1844         }
1845 
1846         // Check if host is up and running
1847         if (dbusUtility::isHostRunning())
1848         {
1849             if (!jsonUtility::isFruReplaceableAtRuntime(m_parsedJson,
1850                                                         l_fruPath))
1851             {
1852                 logging::logMessage(
1853                     "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
1854                     std::string(i_dbusObjPath));
1855                 return;
1856             }
1857         }
1858         else if (dbusUtility::isBMCReady())
1859         {
1860             if (!jsonUtility::isFruReplaceableAtStandby(m_parsedJson,
1861                                                         l_fruPath) &&
1862                 (!jsonUtility::isFruReplaceableAtRuntime(m_parsedJson,
1863                                                          l_fruPath)))
1864             {
1865                 logging::logMessage(
1866                     "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
1867                     std::string(i_dbusObjPath));
1868                 return;
1869             }
1870         }
1871 
1872         // Set collection Status as InProgress. Since it's an intermediate state
1873         // D-bus set-property call is good enough to update the status.
1874         const std::string& l_collStatusProp = "Status";
1875 
1876         if (!dbusUtility::writeDbusProperty(
1877                 jsonUtility::getServiceName(m_parsedJson,
1878                                             std::string(i_dbusObjPath)),
1879                 std::string(i_dbusObjPath), constants::vpdCollectionInterface,
1880                 l_collStatusProp,
1881                 types::DbusVariantType{constants::vpdCollectionInProgress}))
1882         {
1883             logging::logMessage(
1884                 "Unable to set collection Status as InProgress for " +
1885                 std::string(i_dbusObjPath) +
1886                 ". Continue single FRU VPD collection.");
1887         }
1888 
1889         // Parse VPD
1890         types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
1891 
1892         // If l_parsedVpd is pointing to std::monostate
1893         if (l_parsedVpd.index() == 0)
1894         {
1895             throw std::runtime_error(
1896                 "VPD parsing failed for " + std::string(i_dbusObjPath));
1897         }
1898 
1899         // Get D-bus object map from worker class
1900         types::ObjectMap l_dbusObjectMap;
1901         populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
1902 
1903         if (l_dbusObjectMap.empty())
1904         {
1905             throw std::runtime_error(
1906                 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
1907                 std::string(i_dbusObjPath));
1908         }
1909 
1910         // Call PIM's Notify method
1911         if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
1912         {
1913             throw std::runtime_error(
1914                 "Notify PIM failed. Single FRU VPD collection failed for " +
1915                 std::string(i_dbusObjPath));
1916         }
1917     }
1918     catch (const std::exception& l_error)
1919     {
1920         // Notify FRU's VPD collection Status as Failed
1921         if (!dbusUtility::notifyFRUCollectionStatus(
1922                 std::string(i_dbusObjPath), constants::vpdCollectionFailed))
1923         {
1924             logging::logMessage(
1925                 "Call to PIM Notify method failed to update collection Status as Failed for " +
1926                 std::string(i_dbusObjPath));
1927         }
1928 
1929         // TODO: Log PEL
1930         logging::logMessage(std::string(l_error.what()));
1931     }
1932 }
1933 
1934 void Worker::setCollectionStatusProperty(
1935     const std::string& i_vpdPath, const std::string& i_value) const noexcept
1936 {
1937     try
1938     {
1939         if (i_vpdPath.empty())
1940         {
1941             throw std::runtime_error(
1942                 "Given path is empty. Can't set collection Status property");
1943         }
1944 
1945         types::ObjectMap l_objectInterfaceMap;
1946 
1947         if (m_parsedJson["frus"].contains(i_vpdPath))
1948         {
1949             for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1950             {
1951                 sdbusplus::message::object_path l_fruObjectPath(
1952                     l_Fru["inventoryPath"]);
1953 
1954                 types::PropertyMap l_propertyValueMap;
1955                 l_propertyValueMap.emplace("Status", i_value);
1956 
1957                 types::InterfaceMap l_interfaces;
1958                 vpdSpecificUtility::insertOrMerge(
1959                     l_interfaces, constants::vpdCollectionInterface,
1960                     move(l_propertyValueMap));
1961 
1962                 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1963                                              std::move(l_interfaces));
1964             }
1965         }
1966         else
1967         {
1968             // consider it as an inventory path.
1969             if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1970             {
1971                 throw std::runtime_error(
1972                     "Invalid inventory path: " + i_vpdPath +
1973                     ". Can't set collection Status property");
1974             }
1975 
1976             types::PropertyMap l_propertyValueMap;
1977             l_propertyValueMap.emplace("Status", i_value);
1978 
1979             types::InterfaceMap l_interfaces;
1980             vpdSpecificUtility::insertOrMerge(l_interfaces,
1981                                               constants::vpdCollectionInterface,
1982                                               move(l_propertyValueMap));
1983 
1984             l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1985         }
1986 
1987         // Notify PIM
1988         if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1989         {
1990             throw DbusException(
1991                 std::string(__FUNCTION__) +
1992                 "Call to PIM failed while setting collection Status property for path " +
1993                 i_vpdPath);
1994         }
1995     }
1996     catch (const std::exception& l_ex)
1997     {
1998         EventLogger::createSyncPel(
1999             EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
2000             __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
2001             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
2002     }
2003 }
2004 } // namespace vpd
2005