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