xref: /openbmc/openpower-vpd-parser/vpd-manager/src/worker.cpp (revision 1aad78345651f03c08274f2f0c8b3f5bf600ccd7)
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         try
500         {
501             m_parsedJson = jsonUtility::getParsedJson(systemJson);
502         }
503         catch (const nlohmann::json::parse_error& ex)
504         {
505             throw(JsonException("Json parsing failed", systemJson));
506         }
507     }
508 
509     std::string devTreeFromJson;
510     if (m_parsedJson.contains("devTree"))
511     {
512         devTreeFromJson = m_parsedJson["devTree"];
513 
514         if (devTreeFromJson.empty())
515         {
516             // TODO:: Log a predictive PEL
517             logging::logMessage(
518                 "Mandatory value for device tree missing from JSON[" +
519                 std::string(INVENTORY_JSON_SYM_LINK) + "]");
520         }
521     }
522 
523     auto fitConfigVal = readFitConfigValue();
524 
525     if (devTreeFromJson.empty() ||
526         fitConfigVal.find(devTreeFromJson) != std::string::npos)
527     { // Skipping setting device tree as either devtree info is missing from
528       // Json or it is rightly set.
529 
530         // avoid setting symlink on every reboot.
531         if (!m_isSymlinkPresent)
532         {
533             setJsonSymbolicLink(systemJson);
534         }
535 
536         if (isSystemVPDOnDBus() &&
537             jsonUtility::isBackupAndRestoreRequired(m_parsedJson))
538         {
539             performBackupAndRestore(parsedVpdMap);
540         }
541 
542         // proceed to publish system VPD.
543         publishSystemVPD(parsedVpdMap);
544         return;
545     }
546 
547     setEnvAndReboot("fitconfig", devTreeFromJson);
548     exit(EXIT_SUCCESS);
549 }
550 
populateIPZVPDpropertyMap(types::InterfaceMap & interfacePropMap,const types::IPZKwdValueMap & keyordValueMap,const std::string & interfaceName)551 void Worker::populateIPZVPDpropertyMap(
552     types::InterfaceMap& interfacePropMap,
553     const types::IPZKwdValueMap& keyordValueMap,
554     const std::string& interfaceName)
555 {
556     types::PropertyMap propertyValueMap;
557     for (const auto& kwdVal : keyordValueMap)
558     {
559         auto kwd = kwdVal.first;
560 
561         if (kwd[0] == '#')
562         {
563             kwd = std::string("PD_") + kwd[1];
564         }
565         else if (isdigit(kwd[0]))
566         {
567             kwd = std::string("N_") + kwd;
568         }
569 
570         types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end());
571         propertyValueMap.emplace(move(kwd), move(value));
572     }
573 
574     if (!propertyValueMap.empty())
575     {
576         interfacePropMap.emplace(interfaceName, propertyValueMap);
577     }
578 }
579 
populateKwdVPDpropertyMap(const types::KeywordVpdMap & keyordVPDMap,types::InterfaceMap & interfaceMap)580 void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
581                                        types::InterfaceMap& interfaceMap)
582 {
583     for (const auto& kwdValMap : keyordVPDMap)
584     {
585         types::PropertyMap propertyValueMap;
586         auto kwd = kwdValMap.first;
587 
588         if (kwd[0] == '#')
589         {
590             kwd = std::string("PD_") + kwd[1];
591         }
592         else if (isdigit(kwd[0]))
593         {
594             kwd = std::string("N_") + kwd;
595         }
596 
597         if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second))
598         {
599             types::BinaryVector value((*keywordValue).begin(),
600                                       (*keywordValue).end());
601             propertyValueMap.emplace(move(kwd), move(value));
602         }
603         else if (auto keywordValue = get_if<std::string>(&kwdValMap.second))
604         {
605             types::BinaryVector value((*keywordValue).begin(),
606                                       (*keywordValue).end());
607             propertyValueMap.emplace(move(kwd), move(value));
608         }
609         else if (auto keywordValue = get_if<size_t>(&kwdValMap.second))
610         {
611             if (kwd == "MemorySizeInKB")
612             {
613                 types::PropertyMap memProp;
614                 memProp.emplace(move(kwd), ((*keywordValue)));
615                 interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm",
616                                      move(memProp));
617                 continue;
618             }
619             else
620             {
621                 logging::logMessage(
622                     "Unknown Keyword =" + kwd + " found in keyword VPD map");
623                 continue;
624             }
625         }
626         else
627         {
628             logging::logMessage(
629                 "Unknown variant type found in keyword VPD map.");
630             continue;
631         }
632 
633         if (!propertyValueMap.empty())
634         {
635             vpdSpecificUtility::insertOrMerge(
636                 interfaceMap, constants::kwdVpdInf, move(propertyValueMap));
637         }
638     }
639 }
640 
populateInterfaces(const nlohmann::json & interfaceJson,types::InterfaceMap & interfaceMap,const types::VPDMapVariant & parsedVpdMap)641 void Worker::populateInterfaces(const nlohmann::json& interfaceJson,
642                                 types::InterfaceMap& interfaceMap,
643                                 const types::VPDMapVariant& parsedVpdMap)
644 {
645     for (const auto& interfacesPropPair : interfaceJson.items())
646     {
647         const std::string& interface = interfacesPropPair.key();
648         types::PropertyMap propertyMap;
649 
650         for (const auto& propValuePair : interfacesPropPair.value().items())
651         {
652             const std::string property = propValuePair.key();
653 
654             if (propValuePair.value().is_boolean())
655             {
656                 propertyMap.emplace(property,
657                                     propValuePair.value().get<bool>());
658             }
659             else if (propValuePair.value().is_string())
660             {
661                 if (property.compare("LocationCode") == 0 &&
662                     interface.compare("com.ibm.ipzvpd.Location") == 0)
663                 {
664                     std::string value =
665                         vpdSpecificUtility::getExpandedLocationCode(
666                             propValuePair.value().get<std::string>(),
667                             parsedVpdMap);
668                     propertyMap.emplace(property, value);
669 
670                     auto l_locCodeProperty = propertyMap;
671                     vpdSpecificUtility::insertOrMerge(
672                         interfaceMap,
673                         std::string(constants::xyzLocationCodeInf),
674                         move(l_locCodeProperty));
675                 }
676                 else
677                 {
678                     propertyMap.emplace(
679                         property, propValuePair.value().get<std::string>());
680                 }
681             }
682             else if (propValuePair.value().is_array())
683             {
684                 try
685                 {
686                     propertyMap.emplace(
687                         property,
688                         propValuePair.value().get<types::BinaryVector>());
689                 }
690                 catch (const nlohmann::detail::type_error& e)
691                 {
692                     std::cerr << "Type exception: " << e.what() << "\n";
693                 }
694             }
695             else if (propValuePair.value().is_number())
696             {
697                 // For now assume the value is a size_t.  In the future it would
698                 // be nice to come up with a way to get the type from the JSON.
699                 propertyMap.emplace(property,
700                                     propValuePair.value().get<size_t>());
701             }
702             else if (propValuePair.value().is_object())
703             {
704                 const std::string& record =
705                     propValuePair.value().value("recordName", "");
706                 const std::string& keyword =
707                     propValuePair.value().value("keywordName", "");
708                 const std::string& encoding =
709                     propValuePair.value().value("encoding", "");
710 
711                 if (auto ipzVpdMap =
712                         std::get_if<types::IPZVpdMap>(&parsedVpdMap))
713                 {
714                     if (!record.empty() && !keyword.empty() &&
715                         (*ipzVpdMap).count(record) &&
716                         (*ipzVpdMap).at(record).count(keyword))
717                     {
718                         auto encoded = vpdSpecificUtility::encodeKeyword(
719                             ((*ipzVpdMap).at(record).at(keyword)), encoding);
720                         propertyMap.emplace(property, encoded);
721                     }
722                 }
723                 else if (auto kwdVpdMap =
724                              std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
725                 {
726                     if (!keyword.empty() && (*kwdVpdMap).count(keyword))
727                     {
728                         if (auto kwValue = std::get_if<types::BinaryVector>(
729                                 &(*kwdVpdMap).at(keyword)))
730                         {
731                             auto encodedValue =
732                                 vpdSpecificUtility::encodeKeyword(
733                                     std::string((*kwValue).begin(),
734                                                 (*kwValue).end()),
735                                     encoding);
736 
737                             propertyMap.emplace(property, encodedValue);
738                         }
739                         else if (auto kwValue = std::get_if<std::string>(
740                                      &(*kwdVpdMap).at(keyword)))
741                         {
742                             auto encodedValue =
743                                 vpdSpecificUtility::encodeKeyword(
744                                     std::string((*kwValue).begin(),
745                                                 (*kwValue).end()),
746                                     encoding);
747 
748                             propertyMap.emplace(property, encodedValue);
749                         }
750                         else if (auto uintValue = std::get_if<size_t>(
751                                      &(*kwdVpdMap).at(keyword)))
752                         {
753                             propertyMap.emplace(property, *uintValue);
754                         }
755                         else
756                         {
757                             logging::logMessage(
758                                 "Unknown keyword found, Keywrod = " + keyword);
759                         }
760                     }
761                 }
762             }
763         }
764         vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
765                                           move(propertyMap));
766     }
767 }
768 
isCPUIOGoodOnly(const std::string & i_pgKeyword)769 bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
770 {
771     const unsigned char l_io[] = {
772         0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
773         0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
774 
775     // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
776     // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
777     // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
778     // IO.
779     if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
780                constants::SIZE_OF_8EQ_IN_PG) == 0)
781     {
782         return true;
783     }
784 
785     // The CPU is not an IO
786     return false;
787 }
788 
primeInventory(const std::string & i_vpdFilePath)789 bool Worker::primeInventory(const std::string& i_vpdFilePath)
790 {
791     if (i_vpdFilePath.empty())
792     {
793         logging::logMessage("Empty VPD file path given");
794         return false;
795     }
796 
797     if (m_parsedJson.empty())
798     {
799         logging::logMessage("Empty JSON detected for " + i_vpdFilePath);
800         return false;
801     }
802     else if (!m_parsedJson["frus"].contains(i_vpdFilePath))
803     {
804         logging::logMessage("File " + i_vpdFilePath +
805                             ", is not found in the system config JSON file.");
806         return false;
807     }
808 
809     types::ObjectMap l_objectInterfaceMap;
810     for (const auto& l_Fru : m_parsedJson["frus"][i_vpdFilePath])
811     {
812         types::InterfaceMap l_interfaces;
813         sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
814 
815         if (l_Fru.contains("ccin"))
816         {
817             continue;
818         }
819 
820         if (l_Fru.contains("noprime") && l_Fru.value("noprime", false))
821         {
822             continue;
823         }
824 
825         // Reset data under PIM for this FRU only if the FRU is not synthesized
826         // and we handle it's Present property.
827         if (isPresentPropertyHandlingRequired(l_Fru))
828         {
829             // Clear data under PIM if already exists.
830             vpdSpecificUtility::resetDataUnderPIM(
831                 std::string(l_Fru["inventoryPath"]), l_interfaces);
832         }
833 
834         // Add extra interfaces mentioned in the Json config file
835         if (l_Fru.contains("extraInterfaces"))
836         {
837             populateInterfaces(l_Fru["extraInterfaces"], l_interfaces,
838                                std::monostate{});
839         }
840 
841         types::PropertyMap l_propertyValueMap;
842 
843         // Update Present property for this FRU only if we handle Present
844         // property for the FRU.
845         if (isPresentPropertyHandlingRequired(l_Fru))
846         {
847             l_propertyValueMap.emplace("Present", false);
848 
849             // TODO: Present based on file will be taken care in future.
850             // By default present is set to false for FRU at the time of
851             // priming. Once collection goes through, it will be set to true in
852             // that flow.
853             /*if (std::filesystem::exists(i_vpdFilePath))
854             {
855                 l_propertyValueMap["Present"] = true;
856             }*/
857         }
858 
859         vpdSpecificUtility::insertOrMerge(l_interfaces,
860                                           "xyz.openbmc_project.Inventory.Item",
861                                           move(l_propertyValueMap));
862 
863         if (l_Fru.value("inherit", true) &&
864             m_parsedJson.contains("commonInterfaces"))
865         {
866             populateInterfaces(m_parsedJson["commonInterfaces"], l_interfaces,
867                                std::monostate{});
868         }
869 
870         processFunctionalProperty(l_Fru["inventoryPath"], l_interfaces);
871         processEnabledProperty(l_Fru["inventoryPath"], l_interfaces);
872 
873         // Emplace the default state of FRU VPD collection
874         types::PropertyMap l_fruCollectionProperty = {
875             {"CollectionStatus", constants::vpdCollectionNotStarted}};
876 
877         vpdSpecificUtility::insertOrMerge(l_interfaces,
878                                           constants::vpdCollectionInterface,
879                                           std::move(l_fruCollectionProperty));
880 
881         l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
882                                      std::move(l_interfaces));
883     }
884 
885     // Notify PIM
886     if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
887     {
888         logging::logMessage("Call to PIM failed for VPD file " + i_vpdFilePath);
889         return false;
890     }
891 
892     return true;
893 }
894 
processEmbeddedAndSynthesizedFrus(const nlohmann::json & singleFru,types::InterfaceMap & interfaces)895 void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
896                                                types::InterfaceMap& interfaces)
897 {
898     // embedded property(true or false) says whether the subfru is embedded
899     // into the parent fru (or) not. VPD sets Present property only for
900     // embedded frus. If the subfru is not an embedded FRU, the subfru may
901     // or may not be physically present. Those non embedded frus will always
902     // have Present=false irrespective of its physical presence or absence.
903     // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
904     // Present to true for such sub frus.
905     // Eg: ethernet port is embedded into bmc card. So set Present to true
906     // for such sub frus. Also donot populate present property for embedded
907     // subfru which is synthesized. Currently there is no subfru which are
908     // both embedded and synthesized. But still the case is handled here.
909 
910     // Check if its required to handle presence for this FRU.
911     if (singleFru.value("handlePresence", true))
912     {
913         types::PropertyMap presProp;
914         presProp.emplace("Present", true);
915         vpdSpecificUtility::insertOrMerge(
916             interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp));
917     }
918 }
919 
processExtraInterfaces(const nlohmann::json & singleFru,types::InterfaceMap & interfaces,const types::VPDMapVariant & parsedVpdMap)920 void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
921                                     types::InterfaceMap& interfaces,
922                                     const types::VPDMapVariant& parsedVpdMap)
923 {
924     populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
925     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
926     {
927         if (singleFru["extraInterfaces"].contains(
928                 "xyz.openbmc_project.Inventory.Item.Cpu"))
929         {
930             auto itrToRec = (*ipzVpdMap).find("CP00");
931             if (itrToRec == (*ipzVpdMap).end())
932             {
933                 return;
934             }
935 
936             std::string pgKeywordValue;
937             vpdSpecificUtility::getKwVal(itrToRec->second, "PG",
938                                          pgKeywordValue);
939             if (!pgKeywordValue.empty())
940             {
941                 if (isCPUIOGoodOnly(pgKeywordValue))
942                 {
943                     interfaces["xyz.openbmc_project.Inventory.Item"]
944                               ["PrettyName"] = "IO Module";
945                 }
946             }
947         }
948     }
949 }
950 
processCopyRecordFlag(const nlohmann::json & singleFru,const types::VPDMapVariant & parsedVpdMap,types::InterfaceMap & interfaces)951 void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
952                                    const types::VPDMapVariant& parsedVpdMap,
953                                    types::InterfaceMap& interfaces)
954 {
955     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
956     {
957         for (const auto& record : singleFru["copyRecords"])
958         {
959             const std::string& recordName = record;
960             if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
961             {
962                 populateIPZVPDpropertyMap(interfaces,
963                                           (*ipzVpdMap).at(recordName),
964                                           constants::ipzVpdInf + recordName);
965             }
966         }
967     }
968 }
969 
processInheritFlag(const types::VPDMapVariant & parsedVpdMap,types::InterfaceMap & interfaces)970 void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
971                                 types::InterfaceMap& interfaces)
972 {
973     if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
974     {
975         for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
976         {
977             populateIPZVPDpropertyMap(interfaces, kwdValueMap,
978                                       constants::ipzVpdInf + recordName);
979         }
980     }
981     else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
982     {
983         populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
984     }
985 
986     if (m_parsedJson.contains("commonInterfaces"))
987     {
988         populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
989                            parsedVpdMap);
990     }
991 }
992 
processFruWithCCIN(const nlohmann::json & singleFru,const types::VPDMapVariant & parsedVpdMap)993 bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
994                                 const types::VPDMapVariant& parsedVpdMap)
995 {
996     if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
997     {
998         auto itrToRec = (*ipzVPDMap).find("VINI");
999         if (itrToRec == (*ipzVPDMap).end())
1000         {
1001             return false;
1002         }
1003 
1004         std::string ccinFromVpd;
1005         vpdSpecificUtility::getKwVal(itrToRec->second, "CC", ccinFromVpd);
1006         if (ccinFromVpd.empty())
1007         {
1008             return false;
1009         }
1010 
1011         transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
1012                   ::toupper);
1013 
1014         std::vector<std::string> ccinList;
1015         for (std::string ccin : singleFru["ccin"])
1016         {
1017             transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
1018             ccinList.push_back(ccin);
1019         }
1020 
1021         if (ccinList.empty())
1022         {
1023             return false;
1024         }
1025 
1026         if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
1027             ccinList.end())
1028         {
1029             return false;
1030         }
1031     }
1032     return true;
1033 }
1034 
processFunctionalProperty(const std::string & i_inventoryObjPath,types::InterfaceMap & io_interfaces)1035 void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
1036                                        types::InterfaceMap& io_interfaces)
1037 {
1038     if (!dbusUtility::isChassisPowerOn())
1039     {
1040         std::array<const char*, 1> l_operationalStatusInf = {
1041             constants::operationalStatusInf};
1042 
1043         auto mapperObjectMap = dbusUtility::getObjectMap(
1044             i_inventoryObjPath, l_operationalStatusInf);
1045 
1046         // If the object has been found. Check if it is under PIM.
1047         if (mapperObjectMap.size() != 0)
1048         {
1049             for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
1050             {
1051                 if (l_serviceName == constants::pimServiceName)
1052                 {
1053                     // The object is already under PIM. No need to process
1054                     // again. Retain the old value.
1055                     return;
1056                 }
1057             }
1058         }
1059 
1060         // Implies value is not there in D-Bus. Populate it with default
1061         // value "true".
1062         types::PropertyMap l_functionalProp;
1063         l_functionalProp.emplace("Functional", true);
1064         vpdSpecificUtility::insertOrMerge(io_interfaces,
1065                                           constants::operationalStatusInf,
1066                                           move(l_functionalProp));
1067     }
1068 
1069     // if chassis is power on. Functional property should be there on D-Bus.
1070     // Don't process.
1071     return;
1072 }
1073 
processEnabledProperty(const std::string & i_inventoryObjPath,types::InterfaceMap & io_interfaces)1074 void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
1075                                     types::InterfaceMap& io_interfaces)
1076 {
1077     if (!dbusUtility::isChassisPowerOn())
1078     {
1079         std::array<const char*, 1> l_enableInf = {constants::enableInf};
1080 
1081         auto mapperObjectMap =
1082             dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
1083 
1084         // If the object has been found. Check if it is under PIM.
1085         if (mapperObjectMap.size() != 0)
1086         {
1087             for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
1088             {
1089                 if (l_serviceName == constants::pimServiceName)
1090                 {
1091                     // The object is already under PIM. No need to process
1092                     // again. Retain the old value.
1093                     return;
1094                 }
1095             }
1096         }
1097 
1098         // Implies value is not there in D-Bus. Populate it with default
1099         // value "true".
1100         types::PropertyMap l_enabledProp;
1101         l_enabledProp.emplace("Enabled", true);
1102         vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
1103                                           move(l_enabledProp));
1104     }
1105 
1106     // if chassis is power on. Enabled property should be there on D-Bus.
1107     // Don't process.
1108     return;
1109 }
1110 
populateDbus(const types::VPDMapVariant & parsedVpdMap,types::ObjectMap & objectInterfaceMap,const std::string & vpdFilePath)1111 void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
1112                           types::ObjectMap& objectInterfaceMap,
1113                           const std::string& vpdFilePath)
1114 {
1115     if (vpdFilePath.empty())
1116     {
1117         throw std::runtime_error(
1118             "Invalid parameter passed to populateDbus API.");
1119     }
1120 
1121     // JSON config is mandatory for processing of "if". Add "else" for any
1122     // processing without config JSON.
1123     if (!m_parsedJson.empty())
1124     {
1125         types::InterfaceMap interfaces;
1126 
1127         for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
1128         {
1129             const auto& inventoryPath = aFru["inventoryPath"];
1130             sdbusplus::message::object_path fruObjectPath(inventoryPath);
1131             if (aFru.contains("ccin"))
1132             {
1133                 if (!processFruWithCCIN(aFru, parsedVpdMap))
1134                 {
1135                     continue;
1136                 }
1137             }
1138 
1139             if (aFru.value("inherit", true))
1140             {
1141                 processInheritFlag(parsedVpdMap, interfaces);
1142             }
1143 
1144             // If specific record needs to be copied.
1145             if (aFru.contains("copyRecords"))
1146             {
1147                 processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
1148             }
1149 
1150             if (aFru.contains("extraInterfaces"))
1151             {
1152                 // Process extra interfaces w.r.t a FRU.
1153                 processExtraInterfaces(aFru, interfaces, parsedVpdMap);
1154             }
1155 
1156             // Process FRUS which are embedded in the parent FRU and whose VPD
1157             // will be synthesized.
1158             if ((aFru.value("embedded", true)) &&
1159                 (!aFru.value("synthesized", false)))
1160             {
1161                 processEmbeddedAndSynthesizedFrus(aFru, interfaces);
1162             }
1163 
1164             processFunctionalProperty(inventoryPath, interfaces);
1165             processEnabledProperty(inventoryPath, interfaces);
1166 
1167             // Update collection status as successful
1168             types::PropertyMap l_collectionProperty = {
1169                 {"CollectionStatus", constants::vpdCollectionSuccess}};
1170 
1171             vpdSpecificUtility::insertOrMerge(interfaces,
1172                                               constants::vpdCollectionInterface,
1173                                               std::move(l_collectionProperty));
1174 
1175             objectInterfaceMap.emplace(std::move(fruObjectPath),
1176                                        std::move(interfaces));
1177         }
1178     }
1179 }
1180 
createAssetTagString(const types::VPDMapVariant & i_parsedVpdMap)1181 std::string Worker::createAssetTagString(
1182     const types::VPDMapVariant& i_parsedVpdMap)
1183 {
1184     std::string l_assetTag;
1185 
1186     // system VPD will be in IPZ format.
1187     if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
1188     {
1189         auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
1190         if (l_itrToVsys != (*l_parsedVpdMap).end())
1191         {
1192             std::string l_tmKwdValue;
1193             vpdSpecificUtility::getKwVal(l_itrToVsys->second, constants::kwdTM,
1194                                          l_tmKwdValue);
1195 
1196             std::string l_seKwdValue;
1197             vpdSpecificUtility::getKwVal(l_itrToVsys->second, constants::kwdSE,
1198                                          l_seKwdValue);
1199 
1200             l_assetTag = std::string{"Server-"} + l_tmKwdValue +
1201                          std::string{"-"} + l_seKwdValue;
1202         }
1203         else
1204         {
1205             throw std::runtime_error(
1206                 "VSYS record not found in parsed VPD map to create Asset tag.");
1207         }
1208     }
1209     else
1210     {
1211         throw std::runtime_error(
1212             "Invalid VPD type recieved to create Asset tag.");
1213     }
1214 
1215     return l_assetTag;
1216 }
1217 
publishSystemVPD(const types::VPDMapVariant & parsedVpdMap)1218 void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
1219 {
1220     types::ObjectMap objectInterfaceMap;
1221 
1222     if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
1223     {
1224         populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
1225 
1226         try
1227         {
1228             if (m_isFactoryResetDone)
1229             {
1230                 const auto& l_assetTag = createAssetTagString(parsedVpdMap);
1231 
1232                 auto l_itrToSystemPath = objectInterfaceMap.find(
1233                     sdbusplus::message::object_path(constants::systemInvPath));
1234                 if (l_itrToSystemPath == objectInterfaceMap.end())
1235                 {
1236                     throw std::runtime_error(
1237                         "System Path not found in object map.");
1238                 }
1239 
1240                 types::PropertyMap l_assetTagProperty;
1241                 l_assetTagProperty.emplace("AssetTag", l_assetTag);
1242 
1243                 (l_itrToSystemPath->second)
1244                     .emplace(constants::assetTagInf,
1245                              std::move(l_assetTagProperty));
1246             }
1247         }
1248         catch (const std::exception& l_ex)
1249         {
1250             EventLogger::createSyncPel(
1251                 types::ErrorType::InvalidVpdMessage,
1252                 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
1253                 "Asset tag update failed with following error: " +
1254                     std::string(l_ex.what()),
1255                 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1256         }
1257 
1258         // Notify PIM
1259         if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1260         {
1261             throw std::runtime_error("Call to PIM failed for system VPD");
1262         }
1263     }
1264     else
1265     {
1266         throw DataException("Invalid format of parsed VPD map.");
1267     }
1268 }
1269 
processPreAction(const std::string & i_vpdFilePath,const std::string & i_flagToProcess)1270 bool Worker::processPreAction(const std::string& i_vpdFilePath,
1271                               const std::string& i_flagToProcess)
1272 {
1273     if (i_vpdFilePath.empty() || i_flagToProcess.empty())
1274     {
1275         logging::logMessage(
1276             "Invalid input parameter. Abort processing pre action");
1277         return false;
1278     }
1279 
1280     if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
1281                                          i_vpdFilePath, i_flagToProcess)) &&
1282         (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
1283     {
1284         // TODO: Need a way to delete inventory object from Dbus and persisted
1285         // data section in case any FRU is not present or there is any
1286         // problem in collecting it. Once it has been deleted, it can be
1287         // re-created in the flow of priming the inventory. This needs to be
1288         // done either here or in the exception section of "parseAndPublishVPD"
1289         // API. Any failure in the process of collecting FRU will land up in the
1290         // excpetion of "parseAndPublishVPD".
1291 
1292         // If the FRU is not there, clear the VINI/CCIN data.
1293         // Enity manager probes for this keyword to look for this
1294         // FRU, now if the data is persistent on BMC and FRU is
1295         // removed this can lead to ambiguity. Hence clearing this
1296         // Keyword if FRU is absent.
1297         const auto& inventoryPath =
1298             m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
1299                                                             "");
1300 
1301         if (!inventoryPath.empty())
1302         {
1303             types::ObjectMap l_pimObjMap{
1304                 {inventoryPath,
1305                  {{constants::kwdVpdInf,
1306                    {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
1307 
1308             if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
1309             {
1310                 logging::logMessage(
1311                     "Call to PIM failed for file " + i_vpdFilePath);
1312             }
1313         }
1314         else
1315         {
1316             logging::logMessage(
1317                 "Inventory path is empty in Json for file " + i_vpdFilePath);
1318         }
1319 
1320         return false;
1321     }
1322     return true;
1323 }
1324 
processPostAction(const std::string & i_vpdFruPath,const std::string & i_flagToProcess,const std::optional<types::VPDMapVariant> i_parsedVpd)1325 bool Worker::processPostAction(
1326     const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
1327     const std::optional<types::VPDMapVariant> i_parsedVpd)
1328 {
1329     if (i_vpdFruPath.empty() || i_flagToProcess.empty())
1330     {
1331         logging::logMessage(
1332             "Invalid input parameter. Abort processing post action");
1333         return false;
1334     }
1335 
1336     // Check if post action tag is to be triggered in the flow of collection
1337     // based on some CCIN value?
1338     if (m_parsedJson["frus"][i_vpdFruPath]
1339             .at(0)["postAction"][i_flagToProcess]
1340             .contains("ccin"))
1341     {
1342         if (!i_parsedVpd.has_value())
1343         {
1344             logging::logMessage("Empty VPD Map");
1345             return false;
1346         }
1347 
1348         // CCIN match is required to process post action for this FRU as it
1349         // contains the flag.
1350         if (!vpdSpecificUtility::findCcinInVpd(
1351                 m_parsedJson["frus"][i_vpdFruPath].at(
1352                     0)["postAction"]["collection"],
1353                 i_parsedVpd.value()))
1354         {
1355             // If CCIN is not found, implies post action processing is not
1356             // required for this FRU. Let the flow continue.
1357             return true;
1358         }
1359     }
1360 
1361     if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
1362                                         i_vpdFruPath, i_flagToProcess))
1363     {
1364         logging::logMessage(
1365             "Execution of post action failed for path: " + i_vpdFruPath);
1366 
1367         // If post action was required and failed only in that case return
1368         // false. In all other case post action is considered passed.
1369         return false;
1370     }
1371 
1372     return true;
1373 }
1374 
parseVpdFile(const std::string & i_vpdFilePath)1375 types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
1376 {
1377     if (i_vpdFilePath.empty())
1378     {
1379         throw std::runtime_error(
1380             "Empty VPD file path passed to Worker::parseVpdFile. Abort processing");
1381     }
1382 
1383     try
1384     {
1385         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1386                                           "preAction", "collection"))
1387         {
1388             if (!processPreAction(i_vpdFilePath, "collection"))
1389             {
1390                 throw std::runtime_error("Pre-Action failed");
1391             }
1392         }
1393 
1394         if (!std::filesystem::exists(i_vpdFilePath))
1395         {
1396             throw std::runtime_error(
1397                 "Could not find file path " + i_vpdFilePath +
1398                 "Skipping parser trigger for the EEPROM");
1399         }
1400 
1401         std::shared_ptr<Parser> vpdParser =
1402             std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
1403 
1404         types::VPDMapVariant l_parsedVpd = vpdParser->parse();
1405 
1406         // Before returning, as collection is over, check if FRU qualifies for
1407         // any post action in the flow of collection.
1408         // Note: Don't change the order, post action needs to be processed only
1409         // after collection for FRU is successfully done.
1410         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1411                                           "postAction", "collection"))
1412         {
1413             if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
1414             {
1415                 // TODO: Log PEL
1416                 logging::logMessage("Required post action failed for path [" +
1417                                     i_vpdFilePath + "]");
1418             }
1419         }
1420 
1421         return l_parsedVpd;
1422     }
1423     catch (std::exception& l_ex)
1424     {
1425         // If post fail action is required, execute it.
1426         if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1427                                           "postFailAction", "collection"))
1428         {
1429             if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
1430                                                     "collection"))
1431             {
1432                 // TODO: Log PEL
1433                 throw std::runtime_error(
1434                     "VPD parsing failed for " + i_vpdFilePath +
1435                     " due to error: " + l_ex.what() +
1436                     ". Post Fail Action also failed, aborting collection for this FRU");
1437             }
1438         }
1439 
1440         // TODO: Log PEL
1441         throw std::runtime_error("VPD parsing failed for " + i_vpdFilePath +
1442                                  " due to error: " + l_ex.what());
1443     }
1444 }
1445 
parseAndPublishVPD(const std::string & i_vpdFilePath)1446 std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1447     const std::string& i_vpdFilePath)
1448 {
1449     std::string l_inventoryPath{};
1450 
1451     try
1452     {
1453         m_semaphore.acquire();
1454 
1455         // Thread launched.
1456         m_mutex.lock();
1457         m_activeCollectionThreadCount++;
1458         m_mutex.unlock();
1459 
1460         // Set CollectionStatus as InProgress. Since it's an intermediate state
1461         // D-bus set-property call is good enough to update the status.
1462         try
1463         {
1464             l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
1465                 m_parsedJson, i_vpdFilePath);
1466 
1467             dbusUtility::writeDbusProperty(
1468                 jsonUtility::getServiceName(m_parsedJson, l_inventoryPath),
1469                 l_inventoryPath, constants::vpdCollectionInterface,
1470                 "CollectionStatus",
1471                 types::DbusVariantType{constants::vpdCollectionInProgress});
1472         }
1473         catch (const std::exception& l_exception)
1474         {
1475             logging::logMessage(
1476                 "Unable to set CollectionStatus as InProgress for " +
1477                 i_vpdFilePath + ". Error : " + l_exception.what());
1478         }
1479 
1480         const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
1481 
1482         types::ObjectMap objectInterfaceMap;
1483         populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1484 
1485         // logging::logMessage("Dbus sucessfully populated for FRU " +
1486         //                     i_vpdFilePath);
1487 
1488         // Notify PIM
1489         if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1490         {
1491             throw std::runtime_error(
1492                 "Call to PIM failed while publishing VPD.");
1493         }
1494     }
1495     catch (const std::exception& ex)
1496     {
1497         // Notify FRU's VPD CollectionStatus as Failure
1498         if (!dbusUtility::notifyFRUCollectionStatus(
1499                 l_inventoryPath, constants::vpdCollectionFailure))
1500         {
1501             logging::logMessage(
1502                 "Call to PIM Notify method failed to update Collection status as Failure for " +
1503                 i_vpdFilePath);
1504         }
1505 
1506         // handle all the exceptions internally. Return only true/false
1507         // based on status of execution.
1508         if (typeid(ex) == std::type_index(typeid(DataException)))
1509         {
1510             // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1511             // logging error for these cases.
1512             if (vpdSpecificUtility::isPass1Planar())
1513             {
1514                 // Till exceptions are removed from utility method, this needs
1515                 // to be handled in place.
1516                 try
1517                 {
1518                     const std::string& l_invPathLeafValue =
1519                         sdbusplus::message::object_path(
1520                             jsonUtility::getInventoryObjPathFromJson(
1521                                 m_parsedJson, i_vpdFilePath))
1522                             .filename();
1523 
1524                     if ((l_invPathLeafValue.find("pcie_card", 0) !=
1525                          std::string::npos))
1526                     {
1527                         // skip logging any PEL for PCIe cards on pass 1 planar.
1528                         return std::make_tuple(false, i_vpdFilePath);
1529                     }
1530                 }
1531                 catch (const std::exception& l_ex)
1532                 {
1533                     // skip logging any PEL for PCIe cards on pass 1 planar.
1534                     return std::make_tuple(false, i_vpdFilePath);
1535                 }
1536             }
1537 
1538             // TODO: Add custom handling
1539             logging::logMessage(ex.what());
1540         }
1541         else if (typeid(ex) == std::type_index(typeid(EccException)))
1542         {
1543             // TODO: Add custom handling
1544             logging::logMessage(ex.what());
1545         }
1546         else if (typeid(ex) == std::type_index(typeid(JsonException)))
1547         {
1548             // TODO: Add custom handling
1549             logging::logMessage(ex.what());
1550         }
1551         else
1552         {
1553             logging::logMessage(ex.what());
1554         }
1555 
1556         // TODO: Figure out a way to clear data in case of any failure at
1557         // runtime.
1558 
1559         // set present property to false for any error case. In future this will
1560         // be replaced by presence logic.
1561         // Update Present property for this FRU only if we handle Present
1562         // property for the FRU.
1563         if (isPresentPropertyHandlingRequired(
1564                 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1565         {
1566             setPresentProperty(i_vpdFilePath, false);
1567         }
1568 
1569         m_semaphore.release();
1570         return std::make_tuple(false, i_vpdFilePath);
1571     }
1572     m_semaphore.release();
1573     return std::make_tuple(true, i_vpdFilePath);
1574 }
1575 
skipPathForCollection(const std::string & i_vpdFilePath)1576 bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1577 {
1578     if (i_vpdFilePath.empty())
1579     {
1580         return true;
1581     }
1582 
1583     // skip processing of system VPD again as it has been already collected.
1584     if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1585     {
1586         return true;
1587     }
1588 
1589     if (dbusUtility::isChassisPowerOn())
1590     {
1591         // If chassis is powered on, skip collecting FRUs which are
1592         // powerOffOnly.
1593         if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath))
1594         {
1595             return true;
1596         }
1597 
1598         const std::string& l_invPathLeafValue =
1599             sdbusplus::message::object_path(
1600                 jsonUtility::getInventoryObjPathFromJson(m_parsedJson,
1601                                                          i_vpdFilePath))
1602                 .filename();
1603 
1604         if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1605         {
1606             return true;
1607         }
1608     }
1609 
1610     return false;
1611 }
1612 
collectFrusFromJson()1613 void Worker::collectFrusFromJson()
1614 {
1615     // A parsed JSON file should be present to pick FRUs EEPROM paths
1616     if (m_parsedJson.empty())
1617     {
1618         throw std::runtime_error(
1619             "A config JSON is required for processing of FRUs");
1620     }
1621 
1622     const nlohmann::json& listOfFrus =
1623         m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1624 
1625     for (const auto& itemFRUS : listOfFrus.items())
1626     {
1627         const std::string& vpdFilePath = itemFRUS.key();
1628 
1629         if (skipPathForCollection(vpdFilePath))
1630         {
1631             continue;
1632         }
1633 
1634         try
1635         {
1636             std::thread{[vpdFilePath, this]() {
1637                 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
1638 
1639                 m_mutex.lock();
1640                 m_activeCollectionThreadCount--;
1641                 m_mutex.unlock();
1642 
1643                 if (!m_activeCollectionThreadCount)
1644                 {
1645                     m_isAllFruCollected = true;
1646                 }
1647             }}.detach();
1648         }
1649         catch (const std::exception& l_ex)
1650         {
1651             // add vpdFilePath(EEPROM path) to failed list
1652             m_failedEepromPaths.push_front(vpdFilePath);
1653         }
1654     }
1655 }
1656 
1657 // ToDo: Move the API under IBM_SYSTEM
performBackupAndRestore(types::VPDMapVariant & io_srcVpdMap)1658 void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1659 {
1660     try
1661     {
1662         std::string l_backupAndRestoreCfgFilePath =
1663             m_parsedJson.value("backupRestoreConfigPath", "");
1664 
1665         nlohmann::json l_backupAndRestoreCfgJsonObj =
1666             jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath);
1667 
1668         // check if either of "source" or "destination" has inventory path.
1669         // this indicates that this sytem has System VPD on hardware
1670         // and other copy on D-Bus (BMC cache).
1671         if (!l_backupAndRestoreCfgJsonObj.empty() &&
1672             ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1673               l_backupAndRestoreCfgJsonObj["source"].contains(
1674                   "inventoryPath")) ||
1675              (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1676               l_backupAndRestoreCfgJsonObj["destination"].contains(
1677                   "inventoryPath"))))
1678         {
1679             BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1680             auto [l_srcVpdVariant,
1681                   l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1682 
1683             // ToDo: Revisit is this check is required or not.
1684             if (auto l_srcVpdMap =
1685                     std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1686                 l_srcVpdMap && !(*l_srcVpdMap).empty())
1687             {
1688                 io_srcVpdMap = std::move(l_srcVpdVariant);
1689             }
1690         }
1691     }
1692     catch (const std::exception& l_ex)
1693     {
1694         EventLogger::createSyncPel(
1695             types::ErrorType::InvalidVpdMessage,
1696             types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
1697             std::string(
1698                 "Exception caught while backup and restore VPD keyword's.") +
1699                 l_ex.what(),
1700             std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1701     }
1702 }
1703 
deleteFruVpd(const std::string & i_dbusObjPath)1704 void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1705 {
1706     if (i_dbusObjPath.empty())
1707     {
1708         throw std::runtime_error("Given DBus object path is empty.");
1709     }
1710 
1711     const std::string& l_fruPath =
1712         jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1713 
1714     try
1715     {
1716         auto l_presentPropValue = dbusUtility::readDbusProperty(
1717             constants::pimServiceName, i_dbusObjPath,
1718             constants::inventoryItemInf, "Present");
1719 
1720         if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1721         {
1722             if (!(*l_value))
1723             {
1724                 throw std::runtime_error("Given FRU is not present");
1725             }
1726             else
1727             {
1728                 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1729                                                   "preAction", "deletion"))
1730                 {
1731                     if (!processPreAction(l_fruPath, "deletion"))
1732                     {
1733                         throw std::runtime_error("Pre action failed");
1734                     }
1735                 }
1736 
1737                 std::vector<std::string> l_interfaceList{
1738                     constants::operationalStatusInf};
1739 
1740                 types::MapperGetSubTree l_subTreeMap =
1741                     dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1742                                                   l_interfaceList);
1743 
1744                 types::ObjectMap l_objectMap;
1745 
1746                 // Updates VPD specific interfaces property value under PIM for
1747                 // sub FRUs.
1748                 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1749                      l_subTreeMap)
1750                 {
1751                     types::InterfaceMap l_interfaceMap;
1752                     vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1753                                                           l_interfaceMap);
1754                     l_objectMap.emplace(l_objectPath,
1755                                         std::move(l_interfaceMap));
1756                 }
1757 
1758                 types::InterfaceMap l_interfaceMap;
1759                 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1760                                                       l_interfaceMap);
1761 
1762                 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1763 
1764                 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1765                 {
1766                     throw std::runtime_error("Call to PIM failed.");
1767                 }
1768 
1769                 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1770                                                   "postAction", "deletion"))
1771                 {
1772                     if (!processPostAction(l_fruPath, "deletion"))
1773                     {
1774                         throw std::runtime_error("Post action failed");
1775                     }
1776                 }
1777             }
1778         }
1779         else
1780         {
1781             logging::logMessage(
1782                 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1783                 "] as unable to read present property");
1784             return;
1785         }
1786 
1787         logging::logMessage(
1788             "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1789     }
1790     catch (const std::exception& l_ex)
1791     {
1792         if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1793                                           "postFailAction", "deletion"))
1794         {
1795             if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
1796                                                     "deletion"))
1797             {
1798                 logging::logMessage(
1799                     "Post fail action failed for: " + i_dbusObjPath);
1800             }
1801         }
1802 
1803         logging::logMessage("Failed to delete VPD for FRU : " + i_dbusObjPath +
1804                             " error: " + std::string(l_ex.what()));
1805     }
1806 }
1807 
setPresentProperty(const std::string & i_vpdPath,const bool & i_value)1808 void Worker::setPresentProperty(const std::string& i_vpdPath,
1809                                 const bool& i_value)
1810 {
1811     try
1812     {
1813         if (i_vpdPath.empty())
1814         {
1815             throw std::runtime_error(
1816                 "Path is empty. Can't set present property");
1817         }
1818 
1819         types::ObjectMap l_objectInterfaceMap;
1820 
1821         // If the given path is EEPROM path.
1822         if (m_parsedJson["frus"].contains(i_vpdPath))
1823         {
1824             for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1825             {
1826                 sdbusplus::message::object_path l_fruObjectPath(
1827                     l_Fru["inventoryPath"]);
1828 
1829                 types::PropertyMap l_propertyValueMap;
1830                 l_propertyValueMap.emplace("Present", i_value);
1831 
1832                 types::InterfaceMap l_interfaces;
1833                 vpdSpecificUtility::insertOrMerge(l_interfaces,
1834                                                   constants::inventoryItemInf,
1835                                                   move(l_propertyValueMap));
1836 
1837                 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1838                                              std::move(l_interfaces));
1839             }
1840         }
1841         else
1842         {
1843             // consider it as an inventory path.
1844             if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1845             {
1846                 throw std::runtime_error(
1847                     "Invalid inventory path: " + i_vpdPath);
1848             }
1849 
1850             types::PropertyMap l_propertyValueMap;
1851             l_propertyValueMap.emplace("Present", i_value);
1852 
1853             types::InterfaceMap l_interfaces;
1854             vpdSpecificUtility::insertOrMerge(l_interfaces,
1855                                               constants::inventoryItemInf,
1856                                               move(l_propertyValueMap));
1857 
1858             l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1859         }
1860 
1861         // Notify PIM
1862         if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1863         {
1864             throw std::runtime_error(
1865                 "Call to PIM failed while setting present property for path " +
1866                 i_vpdPath);
1867         }
1868     }
1869     catch (const std::exception& l_ex)
1870     {
1871         logging::logMessage(l_ex.what());
1872     }
1873 }
1874 
1875 } // namespace vpd
1876