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