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