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