xref: /openbmc/openpower-vpd-parser/vpd-manager/src/worker.cpp (revision 15a189a90f28de2727653384568ab791d5d6d61d)
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 
Worker(std::string pathToConfigJson,uint8_t i_maxThreadCount)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 
enableMuxChips()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
primeSystemBlueprint()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 
performInitialSetup()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 
readFitConfigValue()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 
isSystemVPDOnDBus() const194 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 
getIMValue(const types::IPZVpdMap & parsedVpd) const219 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 
getHWVersion(const types::IPZVpdMap & parsedVpd) const252 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 
fillVPDMap(const std::string & vpdFilePath,types::VPDMapVariant & vpdMap)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 
getSystemJson(std::string & systemJson,const types::VPDMapVariant & parsedVpdMap)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 
setEnvAndReboot(const std::string & key,const std::string & value)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 
setJsonSymbolicLink(const std::string & i_systemJson)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 
setDeviceTreeAndJson()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 
populateIPZVPDpropertyMap(types::InterfaceMap & interfacePropMap,const types::IPZKwdValueMap & keyordValueMap,const std::string & interfaceName)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 
populateKwdVPDpropertyMap(const types::KeywordVpdMap & keyordVPDMap,types::InterfaceMap & interfaceMap)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 
populateInterfaces(const nlohmann::json & interfaceJson,types::InterfaceMap & interfaceMap,const types::VPDMapVariant & parsedVpdMap)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 
isCPUIOGoodOnly(const std::string & i_pgKeyword)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 
primeInventory(const std::string & i_vpdFilePath)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 
processEmbeddedAndSynthesizedFrus(const nlohmann::json & singleFru,types::InterfaceMap & interfaces)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 
processExtraInterfaces(const nlohmann::json & singleFru,types::InterfaceMap & interfaces,const types::VPDMapVariant & parsedVpdMap)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             const std::string pgKeywordValue{
935                 vpdSpecificUtility::getKwVal(itrToRec->second, "PG")};
936 
937             if (!pgKeywordValue.empty())
938             {
939                 if (isCPUIOGoodOnly(pgKeywordValue))
940                 {
941                     interfaces["xyz.openbmc_project.Inventory.Item"]
942                               ["PrettyName"] = "IO Module";
943                 }
944             }
945             else
946             {
947                 throw std::runtime_error("Failed to get value for keyword PG");
948             }
949         }
950     }
951 }
952 
processCopyRecordFlag(const nlohmann::json & singleFru,const types::VPDMapVariant & parsedVpdMap,types::InterfaceMap & interfaces)953 void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
954                                    const types::VPDMapVariant& parsedVpdMap,
955                                    types::InterfaceMap& interfaces)
956 {
957     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
958     {
959         for (const auto& record : singleFru["copyRecords"])
960         {
961             const std::string& recordName = record;
962             if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
963             {
964                 populateIPZVPDpropertyMap(interfaces,
965                                           (*ipzVpdMap).at(recordName),
966                                           constants::ipzVpdInf + recordName);
967             }
968         }
969     }
970 }
971 
processInheritFlag(const types::VPDMapVariant & parsedVpdMap,types::InterfaceMap & interfaces)972 void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
973                                 types::InterfaceMap& interfaces)
974 {
975     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
976     {
977         for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
978         {
979             populateIPZVPDpropertyMap(interfaces, kwdValueMap,
980                                       constants::ipzVpdInf + recordName);
981         }
982     }
983     else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
984     {
985         populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
986     }
987 
988     if (m_parsedJson.contains("commonInterfaces"))
989     {
990         populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
991                            parsedVpdMap);
992     }
993 }
994 
processFruWithCCIN(const nlohmann::json & singleFru,const types::VPDMapVariant & parsedVpdMap)995 bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
996                                 const types::VPDMapVariant& parsedVpdMap)
997 {
998     if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
999     {
1000         auto itrToRec = (*ipzVPDMap).find("VINI");
1001         if (itrToRec == (*ipzVPDMap).end())
1002         {
1003             return false;
1004         }
1005 
1006         std::string ccinFromVpd{
1007             vpdSpecificUtility::getKwVal(itrToRec->second, "CC")};
1008 
1009         if (ccinFromVpd.empty())
1010         {
1011             return false;
1012         }
1013 
1014         transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
1015                   ::toupper);
1016 
1017         std::vector<std::string> ccinList;
1018         for (std::string ccin : singleFru["ccin"])
1019         {
1020             transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
1021             ccinList.push_back(ccin);
1022         }
1023 
1024         if (ccinList.empty())
1025         {
1026             return false;
1027         }
1028 
1029         if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
1030             ccinList.end())
1031         {
1032             return false;
1033         }
1034     }
1035     return true;
1036 }
1037 
processFunctionalProperty(const std::string & i_inventoryObjPath,types::InterfaceMap & io_interfaces)1038 void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
1039                                        types::InterfaceMap& io_interfaces)
1040 {
1041     if (!dbusUtility::isChassisPowerOn())
1042     {
1043         std::array<const char*, 1> l_operationalStatusInf = {
1044             constants::operationalStatusInf};
1045 
1046         auto mapperObjectMap = dbusUtility::getObjectMap(
1047             i_inventoryObjPath, l_operationalStatusInf);
1048 
1049         // If the object has been found. Check if it is under PIM.
1050         if (mapperObjectMap.size() != 0)
1051         {
1052             for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
1053             {
1054                 if (l_serviceName == constants::pimServiceName)
1055                 {
1056                     // The object is already under PIM. No need to process
1057                     // again. Retain the old value.
1058                     return;
1059                 }
1060             }
1061         }
1062 
1063         // Implies value is not there in D-Bus. Populate it with default
1064         // value "true".
1065         types::PropertyMap l_functionalProp;
1066         l_functionalProp.emplace("Functional", true);
1067         vpdSpecificUtility::insertOrMerge(io_interfaces,
1068                                           constants::operationalStatusInf,
1069                                           move(l_functionalProp));
1070     }
1071 
1072     // if chassis is power on. Functional property should be there on D-Bus.
1073     // Don't process.
1074     return;
1075 }
1076 
processEnabledProperty(const std::string & i_inventoryObjPath,types::InterfaceMap & io_interfaces)1077 void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
1078                                     types::InterfaceMap& io_interfaces)
1079 {
1080     if (!dbusUtility::isChassisPowerOn())
1081     {
1082         std::array<const char*, 1> l_enableInf = {constants::enableInf};
1083 
1084         auto mapperObjectMap =
1085             dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
1086 
1087         // If the object has been found. Check if it is under PIM.
1088         if (mapperObjectMap.size() != 0)
1089         {
1090             for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
1091             {
1092                 if (l_serviceName == constants::pimServiceName)
1093                 {
1094                     // The object is already under PIM. No need to process
1095                     // again. Retain the old value.
1096                     return;
1097                 }
1098             }
1099         }
1100 
1101         // Implies value is not there in D-Bus. Populate it with default
1102         // value "true".
1103         types::PropertyMap l_enabledProp;
1104         l_enabledProp.emplace("Enabled", true);
1105         vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
1106                                           move(l_enabledProp));
1107     }
1108 
1109     // if chassis is power on. Enabled property should be there on D-Bus.
1110     // Don't process.
1111     return;
1112 }
1113 
populateDbus(const types::VPDMapVariant & parsedVpdMap,types::ObjectMap & objectInterfaceMap,const std::string & vpdFilePath)1114 void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
1115                           types::ObjectMap& objectInterfaceMap,
1116                           const std::string& vpdFilePath)
1117 {
1118     if (vpdFilePath.empty())
1119     {
1120         throw std::runtime_error(
1121             "Invalid parameter passed to populateDbus API.");
1122     }
1123 
1124     // JSON config is mandatory for processing of "if". Add "else" for any
1125     // processing without config JSON.
1126     if (!m_parsedJson.empty())
1127     {
1128         types::InterfaceMap interfaces;
1129 
1130         for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
1131         {
1132             const auto& inventoryPath = aFru["inventoryPath"];
1133             sdbusplus::message::object_path fruObjectPath(inventoryPath);
1134             if (aFru.contains("ccin"))
1135             {
1136                 if (!processFruWithCCIN(aFru, parsedVpdMap))
1137                 {
1138                     continue;
1139                 }
1140             }
1141 
1142             if (aFru.value("inherit", true))
1143             {
1144                 processInheritFlag(parsedVpdMap, interfaces);
1145             }
1146 
1147             // If specific record needs to be copied.
1148             if (aFru.contains("copyRecords"))
1149             {
1150                 processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
1151             }
1152 
1153             if (aFru.contains("extraInterfaces"))
1154             {
1155                 // Process extra interfaces w.r.t a FRU.
1156                 processExtraInterfaces(aFru, interfaces, parsedVpdMap);
1157             }
1158 
1159             // Process FRUS which are embedded in the parent FRU and whose VPD
1160             // will be synthesized.
1161             if ((aFru.value("embedded", true)) &&
1162                 (!aFru.value("synthesized", false)))
1163             {
1164                 processEmbeddedAndSynthesizedFrus(aFru, interfaces);
1165             }
1166 
1167             processFunctionalProperty(inventoryPath, interfaces);
1168             processEnabledProperty(inventoryPath, interfaces);
1169 
1170             // Update collection status as successful
1171             types::PropertyMap l_collectionProperty = {
1172                 {"CollectionStatus", constants::vpdCollectionSuccess}};
1173 
1174             vpdSpecificUtility::insertOrMerge(interfaces,
1175                                               constants::vpdCollectionInterface,
1176                                               std::move(l_collectionProperty));
1177 
1178             objectInterfaceMap.emplace(std::move(fruObjectPath),
1179                                        std::move(interfaces));
1180         }
1181     }
1182 }
1183 
createAssetTagString(const types::VPDMapVariant & i_parsedVpdMap)1184 std::string Worker::createAssetTagString(
1185     const types::VPDMapVariant& i_parsedVpdMap)
1186 {
1187     std::string l_assetTag;
1188 
1189     // system VPD will be in IPZ format.
1190     if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
1191     {
1192         auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
1193         if (l_itrToVsys != (*l_parsedVpdMap).end())
1194         {
1195             const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal(
1196                 l_itrToVsys->second, constants::kwdTM)};
1197 
1198             if (l_tmKwdValue.empty())
1199             {
1200                 throw std::runtime_error(
1201                     std::string("Failed to get value for keyword [") +
1202                     constants::kwdTM +
1203                     std::string("] while creating Asset tag."));
1204             }
1205 
1206             const std::string l_seKwdValue{vpdSpecificUtility::getKwVal(
1207                 l_itrToVsys->second, constants::kwdSE)};
1208 
1209             if (l_seKwdValue.empty())
1210             {
1211                 throw std::runtime_error(
1212                     std::string("Failed to get value for keyword [") +
1213                     constants::kwdSE +
1214                     std::string("] while creating Asset tag."));
1215             }
1216 
1217             l_assetTag = std::string{"Server-"} + l_tmKwdValue +
1218                          std::string{"-"} + l_seKwdValue;
1219         }
1220         else
1221         {
1222             throw std::runtime_error(
1223                 "VSYS record not found in parsed VPD map to create Asset tag.");
1224         }
1225     }
1226     else
1227     {
1228         throw std::runtime_error(
1229             "Invalid VPD type recieved to create Asset tag.");
1230     }
1231 
1232     return l_assetTag;
1233 }
1234 
publishSystemVPD(const types::VPDMapVariant & parsedVpdMap)1235 void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
1236 {
1237     types::ObjectMap objectInterfaceMap;
1238 
1239     if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
1240     {
1241         populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
1242 
1243         try
1244         {
1245             if (m_isFactoryResetDone)
1246             {
1247                 const auto& l_assetTag = createAssetTagString(parsedVpdMap);
1248 
1249                 auto l_itrToSystemPath = objectInterfaceMap.find(
1250                     sdbusplus::message::object_path(constants::systemInvPath));
1251                 if (l_itrToSystemPath == objectInterfaceMap.end())
1252                 {
1253                     throw std::runtime_error(
1254                         "System Path not found in object map.");
1255                 }
1256 
1257                 types::PropertyMap l_assetTagProperty;
1258                 l_assetTagProperty.emplace("AssetTag", l_assetTag);
1259 
1260                 (l_itrToSystemPath->second)
1261                     .emplace(constants::assetTagInf,
1262                              std::move(l_assetTagProperty));
1263             }
1264         }
1265         catch (const std::exception& l_ex)
1266         {
1267             EventLogger::createSyncPel(
1268                 types::ErrorType::InvalidVpdMessage,
1269                 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
1270                 "Asset tag update failed with following error: " +
1271                     std::string(l_ex.what()),
1272                 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1273         }
1274 
1275         // Notify PIM
1276         if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1277         {
1278             throw std::runtime_error("Call to PIM failed for system VPD");
1279         }
1280     }
1281     else
1282     {
1283         throw DataException("Invalid format of parsed VPD map.");
1284     }
1285 }
1286 
processPreAction(const std::string & i_vpdFilePath,const std::string & i_flagToProcess)1287 bool Worker::processPreAction(const std::string& i_vpdFilePath,
1288                               const std::string& i_flagToProcess)
1289 {
1290     if (i_vpdFilePath.empty() || i_flagToProcess.empty())
1291     {
1292         logging::logMessage(
1293             "Invalid input parameter. Abort processing pre action");
1294         return false;
1295     }
1296 
1297     if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
1298                                          i_vpdFilePath, i_flagToProcess)) &&
1299         (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
1300     {
1301         // TODO: Need a way to delete inventory object from Dbus and persisted
1302         // data section in case any FRU is not present or there is any
1303         // problem in collecting it. Once it has been deleted, it can be
1304         // re-created in the flow of priming the inventory. This needs to be
1305         // done either here or in the exception section of "parseAndPublishVPD"
1306         // API. Any failure in the process of collecting FRU will land up in the
1307         // excpetion of "parseAndPublishVPD".
1308 
1309         // If the FRU is not there, clear the VINI/CCIN data.
1310         // Enity manager probes for this keyword to look for this
1311         // FRU, now if the data is persistent on BMC and FRU is
1312         // removed this can lead to ambiguity. Hence clearing this
1313         // Keyword if FRU is absent.
1314         const auto& inventoryPath =
1315             m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
1316                                                             "");
1317 
1318         if (!inventoryPath.empty())
1319         {
1320             types::ObjectMap l_pimObjMap{
1321                 {inventoryPath,
1322                  {{constants::kwdVpdInf,
1323                    {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
1324 
1325             if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
1326             {
1327                 logging::logMessage(
1328                     "Call to PIM failed for file " + i_vpdFilePath);
1329             }
1330         }
1331         else
1332         {
1333             logging::logMessage(
1334                 "Inventory path is empty in Json for file " + i_vpdFilePath);
1335         }
1336 
1337         return false;
1338     }
1339     return true;
1340 }
1341 
processPostAction(const std::string & i_vpdFruPath,const std::string & i_flagToProcess,const std::optional<types::VPDMapVariant> i_parsedVpd)1342 bool Worker::processPostAction(
1343     const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
1344     const std::optional<types::VPDMapVariant> i_parsedVpd)
1345 {
1346     if (i_vpdFruPath.empty() || i_flagToProcess.empty())
1347     {
1348         logging::logMessage(
1349             "Invalid input parameter. Abort processing post action");
1350         return false;
1351     }
1352 
1353     // Check if post action tag is to be triggered in the flow of collection
1354     // based on some CCIN value?
1355     if (m_parsedJson["frus"][i_vpdFruPath]
1356             .at(0)["postAction"][i_flagToProcess]
1357             .contains("ccin"))
1358     {
1359         if (!i_parsedVpd.has_value())
1360         {
1361             logging::logMessage("Empty VPD Map");
1362             return false;
1363         }
1364 
1365         // CCIN match is required to process post action for this FRU as it
1366         // contains the flag.
1367         if (!vpdSpecificUtility::findCcinInVpd(
1368                 m_parsedJson["frus"][i_vpdFruPath].at(
1369                     0)["postAction"]["collection"],
1370                 i_parsedVpd.value()))
1371         {
1372             // If CCIN is not found, implies post action processing is not
1373             // required for this FRU. Let the flow continue.
1374             return true;
1375         }
1376     }
1377 
1378     if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
1379                                         i_vpdFruPath, i_flagToProcess))
1380     {
1381         logging::logMessage(
1382             "Execution of post action failed for path: " + i_vpdFruPath);
1383 
1384         // If post action was required and failed only in that case return
1385         // false. In all other case post action is considered passed.
1386         return false;
1387     }
1388 
1389     return true;
1390 }
1391 
parseVpdFile(const std::string & i_vpdFilePath)1392 types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
1393 {
1394     if (i_vpdFilePath.empty())
1395     {
1396         throw std::runtime_error(
1397             "Empty VPD file path passed to Worker::parseVpdFile. Abort processing");
1398     }
1399 
1400     try
1401     {
1402         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1403                                           "preAction", "collection"))
1404         {
1405             if (!processPreAction(i_vpdFilePath, "collection"))
1406             {
1407                 throw std::runtime_error("Pre-Action failed");
1408             }
1409         }
1410 
1411         if (!std::filesystem::exists(i_vpdFilePath))
1412         {
1413             throw std::runtime_error(
1414                 "Could not find file path " + i_vpdFilePath +
1415                 "Skipping parser trigger for the EEPROM");
1416         }
1417 
1418         std::shared_ptr<Parser> vpdParser =
1419             std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
1420 
1421         types::VPDMapVariant l_parsedVpd = vpdParser->parse();
1422 
1423         // Before returning, as collection is over, check if FRU qualifies for
1424         // any post action in the flow of collection.
1425         // Note: Don't change the order, post action needs to be processed only
1426         // after collection for FRU is successfully done.
1427         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1428                                           "postAction", "collection"))
1429         {
1430             if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
1431             {
1432                 // TODO: Log PEL
1433                 logging::logMessage("Required post action failed for path [" +
1434                                     i_vpdFilePath + "]");
1435             }
1436         }
1437 
1438         return l_parsedVpd;
1439     }
1440     catch (std::exception& l_ex)
1441     {
1442         // If post fail action is required, execute it.
1443         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1444                                           "postFailAction", "collection"))
1445         {
1446             if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
1447                                                     "collection"))
1448             {
1449                 // TODO: Log PEL
1450                 throw std::runtime_error(
1451                     "VPD parsing failed for " + i_vpdFilePath +
1452                     " due to error: " + l_ex.what() +
1453                     ". Post Fail Action also failed, aborting collection for this FRU");
1454             }
1455         }
1456 
1457         // TODO: Log PEL
1458         throw std::runtime_error("VPD parsing failed for " + i_vpdFilePath +
1459                                  " due to error: " + l_ex.what());
1460     }
1461 }
1462 
parseAndPublishVPD(const std::string & i_vpdFilePath)1463 std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1464     const std::string& i_vpdFilePath)
1465 {
1466     std::string l_inventoryPath{};
1467 
1468     try
1469     {
1470         m_semaphore.acquire();
1471 
1472         // Thread launched.
1473         m_mutex.lock();
1474         m_activeCollectionThreadCount++;
1475         m_mutex.unlock();
1476 
1477         // Set CollectionStatus as InProgress. Since it's an intermediate state
1478         // D-bus set-property call is good enough to update the status.
1479         l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
1480             m_parsedJson, i_vpdFilePath);
1481 
1482         if (!l_inventoryPath.empty())
1483         {
1484             if (!dbusUtility::writeDbusProperty(
1485                     jsonUtility::getServiceName(m_parsedJson, l_inventoryPath),
1486                     l_inventoryPath, constants::vpdCollectionInterface,
1487                     "CollectionStatus",
1488                     types::DbusVariantType{constants::vpdCollectionInProgress}))
1489             {
1490                 logging::logMessage(
1491                     "Unable to set CollectionStatus as InProgress for " +
1492                     i_vpdFilePath + ". Error : " + "DBus write failed");
1493             }
1494         }
1495 
1496         const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
1497 
1498         types::ObjectMap objectInterfaceMap;
1499         populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1500 
1501         // logging::logMessage("Dbus sucessfully populated for FRU " +
1502         //                     i_vpdFilePath);
1503 
1504         // Notify PIM
1505         if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1506         {
1507             throw std::runtime_error(
1508                 "Call to PIM failed while publishing VPD.");
1509         }
1510     }
1511     catch (const std::exception& ex)
1512     {
1513         // Notify FRU's VPD CollectionStatus as Failure
1514         if (!dbusUtility::notifyFRUCollectionStatus(
1515                 l_inventoryPath, constants::vpdCollectionFailure))
1516         {
1517             logging::logMessage(
1518                 "Call to PIM Notify method failed to update Collection status as Failure for " +
1519                 i_vpdFilePath);
1520         }
1521 
1522         // handle all the exceptions internally. Return only true/false
1523         // based on status of execution.
1524         if (typeid(ex) == std::type_index(typeid(DataException)))
1525         {
1526             // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1527             // logging error for these cases.
1528             if (vpdSpecificUtility::isPass1Planar())
1529             {
1530                 const std::string& l_invPathLeafValue =
1531                     sdbusplus::message::object_path(
1532                         jsonUtility::getInventoryObjPathFromJson(m_parsedJson,
1533                                                                  i_vpdFilePath))
1534                         .filename();
1535 
1536                 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1537                      std::string::npos))
1538                 {
1539                     // skip logging any PEL for PCIe cards on pass 1 planar.
1540                     return std::make_tuple(false, i_vpdFilePath);
1541                 }
1542             }
1543 
1544             // TODO: Add custom handling
1545             logging::logMessage(ex.what());
1546         }
1547         else if (typeid(ex) == std::type_index(typeid(EccException)))
1548         {
1549             // TODO: Add custom handling
1550             logging::logMessage(ex.what());
1551         }
1552         else if (typeid(ex) == std::type_index(typeid(JsonException)))
1553         {
1554             // TODO: Add custom handling
1555             logging::logMessage(ex.what());
1556         }
1557         else
1558         {
1559             logging::logMessage(ex.what());
1560         }
1561 
1562         // TODO: Figure out a way to clear data in case of any failure at
1563         // runtime.
1564 
1565         // set present property to false for any error case. In future this will
1566         // be replaced by presence logic.
1567         // Update Present property for this FRU only if we handle Present
1568         // property for the FRU.
1569         if (isPresentPropertyHandlingRequired(
1570                 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1571         {
1572             setPresentProperty(i_vpdFilePath, false);
1573         }
1574 
1575         m_semaphore.release();
1576         return std::make_tuple(false, i_vpdFilePath);
1577     }
1578     m_semaphore.release();
1579     return std::make_tuple(true, i_vpdFilePath);
1580 }
1581 
skipPathForCollection(const std::string & i_vpdFilePath)1582 bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1583 {
1584     if (i_vpdFilePath.empty())
1585     {
1586         return true;
1587     }
1588 
1589     // skip processing of system VPD again as it has been already collected.
1590     if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1591     {
1592         return true;
1593     }
1594 
1595     if (dbusUtility::isChassisPowerOn())
1596     {
1597         // If chassis is powered on, skip collecting FRUs which are
1598         // powerOffOnly.
1599         if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath))
1600         {
1601             return true;
1602         }
1603 
1604         const std::string& l_invPathLeafValue =
1605             sdbusplus::message::object_path(
1606                 jsonUtility::getInventoryObjPathFromJson(m_parsedJson,
1607                                                          i_vpdFilePath))
1608                 .filename();
1609 
1610         if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1611         {
1612             return true;
1613         }
1614     }
1615 
1616     return false;
1617 }
1618 
collectFrusFromJson()1619 void Worker::collectFrusFromJson()
1620 {
1621     // A parsed JSON file should be present to pick FRUs EEPROM paths
1622     if (m_parsedJson.empty())
1623     {
1624         throw std::runtime_error(
1625             "A config JSON is required for processing of FRUs");
1626     }
1627 
1628     const nlohmann::json& listOfFrus =
1629         m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1630 
1631     for (const auto& itemFRUS : listOfFrus.items())
1632     {
1633         const std::string& vpdFilePath = itemFRUS.key();
1634 
1635         if (skipPathForCollection(vpdFilePath))
1636         {
1637             continue;
1638         }
1639 
1640         try
1641         {
1642             std::thread{[vpdFilePath, this]() {
1643                 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
1644 
1645                 m_mutex.lock();
1646                 m_activeCollectionThreadCount--;
1647                 m_mutex.unlock();
1648 
1649                 if (!m_activeCollectionThreadCount)
1650                 {
1651                     m_isAllFruCollected = true;
1652                 }
1653             }}.detach();
1654         }
1655         catch (const std::exception& l_ex)
1656         {
1657             // add vpdFilePath(EEPROM path) to failed list
1658             m_failedEepromPaths.push_front(vpdFilePath);
1659         }
1660     }
1661 }
1662 
1663 // ToDo: Move the API under IBM_SYSTEM
performBackupAndRestore(types::VPDMapVariant & io_srcVpdMap)1664 void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1665 {
1666     try
1667     {
1668         std::string l_backupAndRestoreCfgFilePath =
1669             m_parsedJson.value("backupRestoreConfigPath", "");
1670 
1671         nlohmann::json l_backupAndRestoreCfgJsonObj =
1672             jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath);
1673 
1674         if (l_backupAndRestoreCfgJsonObj.empty())
1675         {
1676             throw JsonException("JSON parsing failed",
1677                                 l_backupAndRestoreCfgFilePath);
1678         }
1679 
1680         // check if either of "source" or "destination" has inventory path.
1681         // this indicates that this sytem has System VPD on hardware
1682         // and other copy on D-Bus (BMC cache).
1683         if (!l_backupAndRestoreCfgJsonObj.empty() &&
1684             ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1685               l_backupAndRestoreCfgJsonObj["source"].contains(
1686                   "inventoryPath")) ||
1687              (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1688               l_backupAndRestoreCfgJsonObj["destination"].contains(
1689                   "inventoryPath"))))
1690         {
1691             BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1692             auto [l_srcVpdVariant,
1693                   l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1694 
1695             // ToDo: Revisit is this check is required or not.
1696             if (auto l_srcVpdMap =
1697                     std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1698                 l_srcVpdMap && !(*l_srcVpdMap).empty())
1699             {
1700                 io_srcVpdMap = std::move(l_srcVpdVariant);
1701             }
1702         }
1703     }
1704     catch (const std::exception& l_ex)
1705     {
1706         EventLogger::createSyncPel(
1707             EventLogger::getErrorType(l_ex), types::SeverityType::Informational,
1708             __FILE__, __FUNCTION__, 0,
1709             std::string(
1710                 "Exception caught while backup and restore VPD keyword's.") +
1711                 EventLogger::getErrorMsg(l_ex),
1712             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1713     }
1714 }
1715 
deleteFruVpd(const std::string & i_dbusObjPath)1716 void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1717 {
1718     if (i_dbusObjPath.empty())
1719     {
1720         throw std::runtime_error("Given DBus object path is empty.");
1721     }
1722 
1723     const std::string& l_fruPath =
1724         jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1725 
1726     try
1727     {
1728         auto l_presentPropValue = dbusUtility::readDbusProperty(
1729             constants::pimServiceName, i_dbusObjPath,
1730             constants::inventoryItemInf, "Present");
1731 
1732         if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1733         {
1734             if (!(*l_value))
1735             {
1736                 throw std::runtime_error("Given FRU is not present");
1737             }
1738             else
1739             {
1740                 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1741                                                   "preAction", "deletion"))
1742                 {
1743                     if (!processPreAction(l_fruPath, "deletion"))
1744                     {
1745                         throw std::runtime_error("Pre action failed");
1746                     }
1747                 }
1748 
1749                 std::vector<std::string> l_interfaceList{
1750                     constants::operationalStatusInf};
1751 
1752                 types::MapperGetSubTree l_subTreeMap =
1753                     dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1754                                                   l_interfaceList);
1755 
1756                 types::ObjectMap l_objectMap;
1757 
1758                 // Updates VPD specific interfaces property value under PIM for
1759                 // sub FRUs.
1760                 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1761                      l_subTreeMap)
1762                 {
1763                     types::InterfaceMap l_interfaceMap;
1764                     vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1765                                                           l_interfaceMap);
1766                     l_objectMap.emplace(l_objectPath,
1767                                         std::move(l_interfaceMap));
1768                 }
1769 
1770                 types::InterfaceMap l_interfaceMap;
1771                 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1772                                                       l_interfaceMap);
1773 
1774                 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1775 
1776                 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1777                 {
1778                     throw std::runtime_error("Call to PIM failed.");
1779                 }
1780 
1781                 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1782                                                   "postAction", "deletion"))
1783                 {
1784                     if (!processPostAction(l_fruPath, "deletion"))
1785                     {
1786                         throw std::runtime_error("Post action failed");
1787                     }
1788                 }
1789             }
1790         }
1791         else
1792         {
1793             logging::logMessage(
1794                 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1795                 "] as unable to read present property");
1796             return;
1797         }
1798 
1799         logging::logMessage(
1800             "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1801     }
1802     catch (const std::exception& l_ex)
1803     {
1804         if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1805                                           "postFailAction", "deletion"))
1806         {
1807             if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
1808                                                     "deletion"))
1809             {
1810                 logging::logMessage(
1811                     "Post fail action failed for: " + i_dbusObjPath);
1812             }
1813         }
1814 
1815         logging::logMessage("Failed to delete VPD for FRU : " + i_dbusObjPath +
1816                             " error: " + std::string(l_ex.what()));
1817     }
1818 }
1819 
setPresentProperty(const std::string & i_vpdPath,const bool & i_value)1820 void Worker::setPresentProperty(const std::string& i_vpdPath,
1821                                 const bool& i_value)
1822 {
1823     try
1824     {
1825         if (i_vpdPath.empty())
1826         {
1827             throw std::runtime_error(
1828                 "Path is empty. Can't set present property");
1829         }
1830 
1831         types::ObjectMap l_objectInterfaceMap;
1832 
1833         // If the given path is EEPROM path.
1834         if (m_parsedJson["frus"].contains(i_vpdPath))
1835         {
1836             for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1837             {
1838                 sdbusplus::message::object_path l_fruObjectPath(
1839                     l_Fru["inventoryPath"]);
1840 
1841                 types::PropertyMap l_propertyValueMap;
1842                 l_propertyValueMap.emplace("Present", i_value);
1843 
1844                 types::InterfaceMap l_interfaces;
1845                 vpdSpecificUtility::insertOrMerge(l_interfaces,
1846                                                   constants::inventoryItemInf,
1847                                                   move(l_propertyValueMap));
1848 
1849                 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1850                                              std::move(l_interfaces));
1851             }
1852         }
1853         else
1854         {
1855             // consider it as an inventory path.
1856             if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1857             {
1858                 throw std::runtime_error(
1859                     "Invalid inventory path: " + i_vpdPath);
1860             }
1861 
1862             types::PropertyMap l_propertyValueMap;
1863             l_propertyValueMap.emplace("Present", i_value);
1864 
1865             types::InterfaceMap l_interfaces;
1866             vpdSpecificUtility::insertOrMerge(l_interfaces,
1867                                               constants::inventoryItemInf,
1868                                               move(l_propertyValueMap));
1869 
1870             l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1871         }
1872 
1873         // Notify PIM
1874         if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1875         {
1876             throw std::runtime_error(
1877                 "Call to PIM failed while setting present property for path " +
1878                 i_vpdPath);
1879         }
1880     }
1881     catch (const std::exception& l_ex)
1882     {
1883         logging::logMessage(l_ex.what());
1884     }
1885 }
1886 
1887 } // namespace vpd
1888