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