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