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