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