blob: 04f060f7aa5783eff5c3966111e9d27d740ae69a [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,
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001178 const std::string& i_flagToProcess,
1179 uint16_t& i_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001180{
1181 if (i_vpdFilePath.empty() || i_flagToProcess.empty())
1182 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001183 i_errCode = error_code::INVALID_INPUT_PARAMETER;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001184 return false;
1185 }
1186
1187 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001188 i_vpdFilePath, i_flagToProcess,
1189 i_errCode)) &&
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001190 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
1191 {
1192 // TODO: Need a way to delete inventory object from Dbus and persisted
1193 // data section in case any FRU is not present or there is any
1194 // problem in collecting it. Once it has been deleted, it can be
1195 // re-created in the flow of priming the inventory. This needs to be
1196 // done either here or in the exception section of "parseAndPublishVPD"
1197 // API. Any failure in the process of collecting FRU will land up in the
1198 // excpetion of "parseAndPublishVPD".
1199
1200 // If the FRU is not there, clear the VINI/CCIN data.
1201 // Enity manager probes for this keyword to look for this
1202 // FRU, now if the data is persistent on BMC and FRU is
1203 // removed this can lead to ambiguity. Hence clearing this
1204 // Keyword if FRU is absent.
1205 const auto& inventoryPath =
1206 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
1207 "");
1208
1209 if (!inventoryPath.empty())
1210 {
1211 types::ObjectMap l_pimObjMap{
1212 {inventoryPath,
1213 {{constants::kwdVpdInf,
1214 {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
1215
1216 if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
1217 {
1218 logging::logMessage(
1219 "Call to PIM failed for file " + i_vpdFilePath);
1220 }
1221 }
1222 else
1223 {
1224 logging::logMessage(
1225 "Inventory path is empty in Json for file " + i_vpdFilePath);
1226 }
1227
1228 return false;
1229 }
1230 return true;
1231}
1232
1233bool Worker::processPostAction(
1234 const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
1235 const std::optional<types::VPDMapVariant> i_parsedVpd)
1236{
1237 if (i_vpdFruPath.empty() || i_flagToProcess.empty())
1238 {
1239 logging::logMessage(
1240 "Invalid input parameter. Abort processing post action");
1241 return false;
1242 }
1243
1244 // Check if post action tag is to be triggered in the flow of collection
1245 // based on some CCIN value?
1246 if (m_parsedJson["frus"][i_vpdFruPath]
1247 .at(0)["postAction"][i_flagToProcess]
1248 .contains("ccin"))
1249 {
1250 if (!i_parsedVpd.has_value())
1251 {
1252 logging::logMessage("Empty VPD Map");
1253 return false;
1254 }
1255
1256 // CCIN match is required to process post action for this FRU as it
1257 // contains the flag.
1258 if (!vpdSpecificUtility::findCcinInVpd(
1259 m_parsedJson["frus"][i_vpdFruPath].at(
1260 0)["postAction"]["collection"],
1261 i_parsedVpd.value()))
1262 {
1263 // If CCIN is not found, implies post action processing is not
1264 // required for this FRU. Let the flow continue.
1265 return true;
1266 }
1267 }
1268
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001269 uint16_t l_errCode = 0;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001270 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001271 i_vpdFruPath, i_flagToProcess,
1272 l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001273 {
1274 logging::logMessage(
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001275 "Execution of post action failed for path: " + i_vpdFruPath +
1276 " . Reason: " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001277
1278 // If post action was required and failed only in that case return
1279 // false. In all other case post action is considered passed.
1280 return false;
1281 }
1282
1283 return true;
1284}
1285
1286types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
1287{
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001288 try
1289 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301290 if (i_vpdFilePath.empty())
1291 {
1292 throw std::runtime_error(
1293 std::string(__FUNCTION__) +
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301294 " Empty VPD file path passed. Abort processing");
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301295 }
1296
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301297 bool isPreActionRequired = false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001298 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1299 "preAction", "collection"))
1300 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301301 isPreActionRequired = true;
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001302 uint16_t l_errCode = 0;
1303 if (!processPreAction(i_vpdFilePath, "collection", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001304 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001305 if (l_errCode == error_code::DEVICE_NOT_PRESENT)
1306 {
1307 logging::logMessage(
1308 vpdSpecificUtility::getErrCodeMsg(l_errCode) +
1309 i_vpdFilePath);
1310 // Presence pin has been read successfully and has been read
1311 // as false, so this is not a failure case, hence returning
1312 // empty variant so that pre action is not marked as failed.
1313 return types::VPDMapVariant{};
1314 }
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301315 throw std::runtime_error(
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001316 std::string(__FUNCTION__) +
1317 " Pre-Action failed with error: " +
1318 vpdSpecificUtility::getErrCodeMsg(l_errCode));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001319 }
1320 }
1321
1322 if (!std::filesystem::exists(i_vpdFilePath))
1323 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301324 if (isPreActionRequired)
1325 {
1326 throw std::runtime_error(
1327 std::string(__FUNCTION__) + " Could not find file path " +
1328 i_vpdFilePath + "Skipping parser trigger for the EEPROM");
1329 }
1330 return types::VPDMapVariant{};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001331 }
1332
1333 std::shared_ptr<Parser> vpdParser =
1334 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
1335
1336 types::VPDMapVariant l_parsedVpd = vpdParser->parse();
1337
1338 // Before returning, as collection is over, check if FRU qualifies for
1339 // any post action in the flow of collection.
1340 // Note: Don't change the order, post action needs to be processed only
1341 // after collection for FRU is successfully done.
1342 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1343 "postAction", "collection"))
1344 {
1345 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
1346 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301347 // Post action was required but failed while executing.
1348 // Behaviour can be undefined.
1349 EventLogger::createSyncPel(
1350 types::ErrorType::InternalFailure,
1351 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
1352 std::string("Required post action failed for path [" +
1353 i_vpdFilePath + "]"),
1354 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001355 }
1356 }
1357
1358 return l_parsedVpd;
1359 }
1360 catch (std::exception& l_ex)
1361 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001362 uint16_t l_errCode = 0;
Souvik Roy37c6bef2025-07-17 00:55:59 -05001363 std::string l_exMsg{
1364 std::string(__FUNCTION__) + " : VPD parsing failed for " +
1365 i_vpdFilePath + " due to error: " + l_ex.what()};
1366
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001367 // If post fail action is required, execute it.
1368 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
Sunny Srivastava4c164382025-01-28 03:17:33 -06001369 "postFailAction", "collection"))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001370 {
1371 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001372 "collection", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001373 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001374 l_exMsg += ". Post fail action also failed. Error : " +
1375 vpdSpecificUtility::getErrCodeMsg(l_errCode) +
1376 " Aborting collection for this FRU.";
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001377 }
1378 }
1379
Souvik Roy37c6bef2025-07-17 00:55:59 -05001380 if (typeid(l_ex) == typeid(DataException))
1381 {
1382 throw DataException(l_exMsg);
1383 }
1384 else if (typeid(l_ex) == typeid(EccException))
1385 {
1386 throw EccException(l_exMsg);
1387 }
1388 throw std::runtime_error(l_exMsg);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001389 }
1390}
1391
Patrick Williams43fedab2025-02-03 14:28:05 -05001392std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1393 const std::string& i_vpdFilePath)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001394{
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001395 std::string l_inventoryPath{};
1396
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001397 try
1398 {
1399 m_semaphore.acquire();
1400
1401 // Thread launched.
1402 m_mutex.lock();
1403 m_activeCollectionThreadCount++;
1404 m_mutex.unlock();
1405
Rekha Aparna017567a2025-08-13 02:07:06 -05001406 uint16_t l_errCode = 0;
1407
1408 // Set CollectionStatus as InProgress. Since it's an intermediate state
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001409 // D-bus set-property call is good enough to update the status.
RekhaAparna011ef21002025-02-18 23:47:36 -06001410 l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
Rekha Aparna017567a2025-08-13 02:07:06 -05001411 m_parsedJson, i_vpdFilePath, l_errCode);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001412
RekhaAparna011ef21002025-02-18 23:47:36 -06001413 if (!l_inventoryPath.empty())
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001414 {
Sunny Srivastava995e1c22025-08-28 03:13:00 -05001415 // save time stamp under PIM.
1416 vpdSpecificUtility::saveTimeStampInPim(l_inventoryPath,
1417 "StartTime");
1418
RekhaAparna01c532b182025-02-19 19:56:49 -06001419 if (!dbusUtility::writeDbusProperty(
RekhaAparna011ef21002025-02-18 23:47:36 -06001420 jsonUtility::getServiceName(m_parsedJson, l_inventoryPath),
1421 l_inventoryPath, constants::vpdCollectionInterface,
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001422 "Status",
RekhaAparna01c532b182025-02-19 19:56:49 -06001423 types::DbusVariantType{constants::vpdCollectionInProgress}))
RekhaAparna011ef21002025-02-18 23:47:36 -06001424 {
1425 logging::logMessage(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001426 "Unable to set collection Status as InProgress for " +
RekhaAparna01c532b182025-02-19 19:56:49 -06001427 i_vpdFilePath + ". Error : " + "DBus write failed");
RekhaAparna011ef21002025-02-18 23:47:36 -06001428 }
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001429 }
Rekha Aparna017567a2025-08-13 02:07:06 -05001430 else if (l_errCode)
1431 {
1432 logging::logMessage(
1433 "Failed to get inventory path for FRU [" + i_vpdFilePath +
1434 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
1435 }
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001436
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001437 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301438 if (!std::holds_alternative<std::monostate>(parsedVpdMap))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001439 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301440 types::ObjectMap objectInterfaceMap;
1441 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1442
Sunny Srivastava995e1c22025-08-28 03:13:00 -05001443 // save end time stamp under PIM.
1444 vpdSpecificUtility::saveTimeStampInPim(l_inventoryPath,
1445 "CompletedTime");
1446
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301447 // Notify PIM
1448 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1449 {
1450 throw std::runtime_error(
1451 std::string(__FUNCTION__) +
1452 "Call to PIM failed while publishing VPD.");
1453 }
1454 }
1455 else
1456 {
1457 logging::logMessage("Empty parsedVpdMap recieved for path [" +
1458 i_vpdFilePath + "]. Check PEL for reason.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001459 }
1460 }
1461 catch (const std::exception& ex)
1462 {
Anupama B R24691d22025-05-21 08:14:15 -05001463 setCollectionStatusProperty(i_vpdFilePath,
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001464 constants::vpdCollectionFailed);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001465
Sunny Srivastava995e1c22025-08-28 03:13:00 -05001466 // save end time stamp under PIM.
1467 vpdSpecificUtility::saveTimeStampInPim(l_inventoryPath,
1468 "CompletedTime");
1469
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001470 // handle all the exceptions internally. Return only true/false
1471 // based on status of execution.
1472 if (typeid(ex) == std::type_index(typeid(DataException)))
1473 {
Rekha Aparna017567a2025-08-13 02:07:06 -05001474 uint16_t l_errCode = 0;
Sunny Srivastava78c91072025-02-05 14:09:50 +05301475 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1476 // logging error for these cases.
1477 if (vpdSpecificUtility::isPass1Planar())
1478 {
Rekha Aparna017567a2025-08-13 02:07:06 -05001479 std::string l_invPath =
1480 jsonUtility::getInventoryObjPathFromJson(
1481 m_parsedJson, i_vpdFilePath, l_errCode);
1482
1483 if (l_errCode != 0)
1484 {
1485 logging::logMessage(
1486 "Failed to get inventory object path from JSON for FRU [" +
1487 i_vpdFilePath + "], error: " +
1488 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1489 }
1490
RekhaAparna011ef21002025-02-18 23:47:36 -06001491 const std::string& l_invPathLeafValue =
Rekha Aparna017567a2025-08-13 02:07:06 -05001492 sdbusplus::message::object_path(l_invPath).filename();
Sunny Srivastava78c91072025-02-05 14:09:50 +05301493
RekhaAparna011ef21002025-02-18 23:47:36 -06001494 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1495 std::string::npos))
Sunny Srivastava78c91072025-02-05 14:09:50 +05301496 {
1497 // skip logging any PEL for PCIe cards on pass 1 planar.
1498 return std::make_tuple(false, i_vpdFilePath);
1499 }
1500 }
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301501 }
Sunny Srivastava78c91072025-02-05 14:09:50 +05301502
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301503 EventLogger::createSyncPel(
Souvik Roy37c6bef2025-07-17 00:55:59 -05001504 EventLogger::getErrorType(ex),
1505 (typeid(ex) == typeid(DataException)) ||
1506 (typeid(ex) == typeid(EccException))
1507 ? types::SeverityType::Warning
1508 : types::SeverityType::Informational,
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301509 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1510 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001511
1512 // TODO: Figure out a way to clear data in case of any failure at
1513 // runtime.
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301514
1515 // set present property to false for any error case. In future this will
1516 // be replaced by presence logic.
Souvik Roy6a9553c2025-02-07 01:16:32 -06001517 // Update Present property for this FRU only if we handle Present
1518 // property for the FRU.
1519 if (isPresentPropertyHandlingRequired(
1520 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1521 {
1522 setPresentProperty(i_vpdFilePath, false);
1523 }
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301524
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001525 m_semaphore.release();
1526 return std::make_tuple(false, i_vpdFilePath);
1527 }
1528 m_semaphore.release();
1529 return std::make_tuple(true, i_vpdFilePath);
1530}
1531
Sunny Srivastava61611752025-02-04 00:29:33 -06001532bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1533{
1534 if (i_vpdFilePath.empty())
1535 {
1536 return true;
1537 }
1538
1539 // skip processing of system VPD again as it has been already collected.
1540 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1541 {
1542 return true;
1543 }
1544
1545 if (dbusUtility::isChassisPowerOn())
1546 {
1547 // If chassis is powered on, skip collecting FRUs which are
1548 // powerOffOnly.
1549 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath))
1550 {
1551 return true;
1552 }
1553
Rekha Aparna017567a2025-08-13 02:07:06 -05001554 uint16_t l_errCode = 0;
1555 std::string l_invPath = jsonUtility::getInventoryObjPathFromJson(
1556 m_parsedJson, i_vpdFilePath, l_errCode);
1557
1558 if (l_errCode)
1559 {
1560 logging::logMessage(
1561 "Failed to get inventory path from JSON for FRU [" +
1562 i_vpdFilePath +
1563 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
1564
1565 return false;
1566 }
1567
Sunny Srivastava61611752025-02-04 00:29:33 -06001568 const std::string& l_invPathLeafValue =
Rekha Aparna017567a2025-08-13 02:07:06 -05001569 sdbusplus::message::object_path(l_invPath).filename();
Sunny Srivastava61611752025-02-04 00:29:33 -06001570
1571 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1572 {
1573 return true;
1574 }
1575 }
1576
1577 return false;
1578}
1579
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001580void Worker::collectFrusFromJson()
1581{
1582 // A parsed JSON file should be present to pick FRUs EEPROM paths
1583 if (m_parsedJson.empty())
1584 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301585 throw JsonException(
1586 std::string(__FUNCTION__) +
1587 ": Config JSON is mandatory for processing of FRUs through this API.",
1588 m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001589 }
1590
1591 const nlohmann::json& listOfFrus =
1592 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1593
1594 for (const auto& itemFRUS : listOfFrus.items())
1595 {
1596 const std::string& vpdFilePath = itemFRUS.key();
1597
Sunny Srivastava61611752025-02-04 00:29:33 -06001598 if (skipPathForCollection(vpdFilePath))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001599 {
1600 continue;
1601 }
1602
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001603 try
1604 {
1605 std::thread{[vpdFilePath, this]() {
1606 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001607
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001608 m_mutex.lock();
1609 m_activeCollectionThreadCount--;
1610 m_mutex.unlock();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001611
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001612 if (!m_activeCollectionThreadCount)
1613 {
1614 m_isAllFruCollected = true;
1615 }
1616 }}.detach();
1617 }
1618 catch (const std::exception& l_ex)
1619 {
1620 // add vpdFilePath(EEPROM path) to failed list
1621 m_failedEepromPaths.push_front(vpdFilePath);
1622 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001623 }
1624}
1625
1626// ToDo: Move the API under IBM_SYSTEM
1627void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1628{
1629 try
1630 {
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001631 uint16_t l_errCode = 0;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001632 std::string l_backupAndRestoreCfgFilePath =
1633 m_parsedJson.value("backupRestoreConfigPath", "");
1634
1635 nlohmann::json l_backupAndRestoreCfgJsonObj =
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001636 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath,
1637 l_errCode);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001638
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001639 if (l_errCode)
RekhaAparna011ef21002025-02-18 23:47:36 -06001640 {
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001641 throw JsonException(
1642 "JSON parsing failed for file [ " +
1643 l_backupAndRestoreCfgFilePath + " ], error : " +
1644 vpdSpecificUtility::getErrCodeMsg(l_errCode),
1645 l_backupAndRestoreCfgFilePath);
RekhaAparna011ef21002025-02-18 23:47:36 -06001646 }
1647
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001648 // check if either of "source" or "destination" has inventory path.
1649 // this indicates that this sytem has System VPD on hardware
1650 // and other copy on D-Bus (BMC cache).
1651 if (!l_backupAndRestoreCfgJsonObj.empty() &&
1652 ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1653 l_backupAndRestoreCfgJsonObj["source"].contains(
1654 "inventoryPath")) ||
1655 (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1656 l_backupAndRestoreCfgJsonObj["destination"].contains(
1657 "inventoryPath"))))
1658 {
1659 BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1660 auto [l_srcVpdVariant,
1661 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1662
1663 // ToDo: Revisit is this check is required or not.
1664 if (auto l_srcVpdMap =
1665 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1666 l_srcVpdMap && !(*l_srcVpdMap).empty())
1667 {
1668 io_srcVpdMap = std::move(l_srcVpdVariant);
1669 }
1670 }
1671 }
1672 catch (const std::exception& l_ex)
1673 {
1674 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301675 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301676 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001677 std::string(
1678 "Exception caught while backup and restore VPD keyword's.") +
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301679 EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001680 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1681 }
1682}
1683
1684void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1685{
1686 if (i_dbusObjPath.empty())
1687 {
1688 throw std::runtime_error("Given DBus object path is empty.");
1689 }
1690
Rekha Aparna0578dd22025-09-02 08:20:21 -05001691 uint16_t l_errCode = 0;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001692 const std::string& l_fruPath =
Rekha Aparna0578dd22025-09-02 08:20:21 -05001693 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath, l_errCode);
1694
1695 if (l_errCode)
1696 {
1697 logging::logMessage(
1698 "Failed to get FRU path for inventory path [" + i_dbusObjPath +
1699 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode) +
1700 " Aborting FRU VPD deletion.");
1701 return;
1702 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001703
1704 try
1705 {
1706 auto l_presentPropValue = dbusUtility::readDbusProperty(
1707 constants::pimServiceName, i_dbusObjPath,
1708 constants::inventoryItemInf, "Present");
1709
1710 if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1711 {
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001712 uint16_t l_errCode = 0;
Souvik Roye9120152025-07-02 08:24:38 -05001713 // check if FRU's Present property is handled by vpd-manager
1714 const auto& l_isFruPresenceHandled =
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001715 jsonUtility::isFruPresenceHandled(m_parsedJson, l_fruPath,
1716 l_errCode);
1717
1718 if (l_errCode)
1719 {
1720 throw std::runtime_error(
1721 "Failed to check if FRU's presence is handled, reason: " +
1722 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1723 }
Souvik Roye9120152025-07-02 08:24:38 -05001724
1725 if (!(*l_value) && l_isFruPresenceHandled)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001726 {
1727 throw std::runtime_error("Given FRU is not present");
1728 }
Souvik Roye9120152025-07-02 08:24:38 -05001729 else if (*l_value && !l_isFruPresenceHandled)
1730 {
1731 throw std::runtime_error(
1732 "Given FRU is present and its presence is not handled by vpd-manager.");
1733 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001734 else
1735 {
1736 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1737 "preAction", "deletion"))
1738 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001739 uint16_t l_errCode = 0;
1740 if (!processPreAction(l_fruPath, "deletion", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001741 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001742 std::string l_msg = "Pre action failed";
1743 if (l_errCode)
1744 {
1745 l_msg +=
1746 " Reason: " +
1747 vpdSpecificUtility::getErrCodeMsg(l_errCode);
1748 }
1749 throw std::runtime_error(l_msg);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001750 }
1751 }
1752
1753 std::vector<std::string> l_interfaceList{
1754 constants::operationalStatusInf};
1755
1756 types::MapperGetSubTree l_subTreeMap =
1757 dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1758 l_interfaceList);
1759
1760 types::ObjectMap l_objectMap;
1761
1762 // Updates VPD specific interfaces property value under PIM for
1763 // sub FRUs.
1764 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1765 l_subTreeMap)
1766 {
1767 types::InterfaceMap l_interfaceMap;
1768 vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1769 l_interfaceMap);
1770 l_objectMap.emplace(l_objectPath,
1771 std::move(l_interfaceMap));
1772 }
1773
1774 types::InterfaceMap l_interfaceMap;
1775 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1776 l_interfaceMap);
1777
1778 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1779
1780 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1781 {
1782 throw std::runtime_error("Call to PIM failed.");
1783 }
1784
1785 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1786 "postAction", "deletion"))
1787 {
1788 if (!processPostAction(l_fruPath, "deletion"))
1789 {
1790 throw std::runtime_error("Post action failed");
1791 }
1792 }
1793 }
1794 }
1795 else
1796 {
1797 logging::logMessage(
1798 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1799 "] as unable to read present property");
1800 return;
1801 }
1802
1803 logging::logMessage(
1804 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1805 }
1806 catch (const std::exception& l_ex)
1807 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001808 uint16_t l_errCode = 0;
1809 std::string l_errMsg =
1810 "Failed to delete VPD for FRU : " + i_dbusObjPath +
1811 " error: " + std::string(l_ex.what());
1812
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001813 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1814 "postFailAction", "deletion"))
1815 {
1816 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001817 "deletion", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001818 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001819 l_errMsg += ". Post fail action also failed, error : " +
1820 vpdSpecificUtility::getErrCodeMsg(l_errCode);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001821 }
1822 }
1823
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001824 logging::logMessage(l_errMsg);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001825 }
1826}
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301827
1828void Worker::setPresentProperty(const std::string& i_vpdPath,
1829 const bool& i_value)
1830{
1831 try
1832 {
1833 if (i_vpdPath.empty())
1834 {
1835 throw std::runtime_error(
1836 "Path is empty. Can't set present property");
1837 }
1838
1839 types::ObjectMap l_objectInterfaceMap;
1840
1841 // If the given path is EEPROM path.
1842 if (m_parsedJson["frus"].contains(i_vpdPath))
1843 {
1844 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1845 {
1846 sdbusplus::message::object_path l_fruObjectPath(
1847 l_Fru["inventoryPath"]);
1848
1849 types::PropertyMap l_propertyValueMap;
1850 l_propertyValueMap.emplace("Present", i_value);
1851
1852 types::InterfaceMap l_interfaces;
1853 vpdSpecificUtility::insertOrMerge(l_interfaces,
1854 constants::inventoryItemInf,
1855 move(l_propertyValueMap));
1856
1857 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1858 std::move(l_interfaces));
1859 }
1860 }
1861 else
1862 {
1863 // consider it as an inventory path.
1864 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1865 {
1866 throw std::runtime_error(
1867 "Invalid inventory path: " + i_vpdPath);
1868 }
1869
1870 types::PropertyMap l_propertyValueMap;
1871 l_propertyValueMap.emplace("Present", i_value);
1872
1873 types::InterfaceMap l_interfaces;
1874 vpdSpecificUtility::insertOrMerge(l_interfaces,
1875 constants::inventoryItemInf,
1876 move(l_propertyValueMap));
1877
1878 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1879 }
1880
1881 // Notify PIM
1882 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1883 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301884 throw DbusException(
1885 std::string(__FUNCTION__) +
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301886 "Call to PIM failed while setting present property for path " +
1887 i_vpdPath);
1888 }
1889 }
1890 catch (const std::exception& l_ex)
1891 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301892 EventLogger::createSyncPel(
1893 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1894 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1895 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301896 }
1897}
1898
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301899void Worker::performVpdRecollection()
1900{
1901 try
1902 {
1903 // Check if system config JSON is present
1904 if (m_parsedJson.empty())
1905 {
1906 throw std::runtime_error(
1907 "System config json object is empty, can't process recollection.");
1908 }
1909
1910 const auto& l_frusReplaceableAtStandby =
1911 jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson);
1912
1913 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1914 {
1915 // ToDo: Add some logic/trace to know the flow to
1916 // collectSingleFruVpd has been directed via
1917 // performVpdRecollection.
1918 collectSingleFruVpd(l_fruInventoryPath);
1919 }
1920 return;
1921 }
1922
1923 catch (const std::exception& l_ex)
1924 {
1925 // TODO Log PEL
1926 logging::logMessage(
1927 "VPD recollection failed with error: " + std::string(l_ex.what()));
1928 }
1929}
1930
1931void Worker::collectSingleFruVpd(
1932 const sdbusplus::message::object_path& i_dbusObjPath)
1933{
Anupama B R48f297b2025-08-13 04:29:06 -05001934 std::string l_fruPath{};
Rekha Aparna0578dd22025-09-02 08:20:21 -05001935 uint16_t l_errCode = 0;
1936
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301937 try
1938 {
1939 // Check if system config JSON is present
1940 if (m_parsedJson.empty())
1941 {
1942 logging::logMessage(
1943 "System config JSON object not present. Single FRU VPD collection is not performed for " +
1944 std::string(i_dbusObjPath));
1945 return;
1946 }
1947
1948 // Get FRU path for the given D-bus object path from JSON
Rekha Aparna0578dd22025-09-02 08:20:21 -05001949 l_fruPath = jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath,
1950 l_errCode);
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301951
1952 if (l_fruPath.empty())
1953 {
Rekha Aparna0578dd22025-09-02 08:20:21 -05001954 if (l_errCode)
1955 {
1956 logging::logMessage(
1957 "Failed to get FRU path for [" +
1958 std::string(i_dbusObjPath) + "], error : " +
1959 vpdSpecificUtility::getErrCodeMsg(l_errCode) +
1960 " Aborting single FRU VPD collection.");
1961 return;
1962 }
1963
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301964 logging::logMessage(
1965 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
1966 std::string(i_dbusObjPath));
1967 return;
1968 }
1969
1970 // Check if host is up and running
1971 if (dbusUtility::isHostRunning())
1972 {
Rekha Aparnaad0db9e2025-09-01 20:29:18 -05001973 uint16_t l_errCode = 0;
1974 bool isFruReplaceableAtRuntime =
1975 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
1976 l_errCode);
1977
1978 if (l_errCode)
1979 {
1980 logging::logMessage(
1981 "Failed to check if FRU is replaceable at runtime for FRU : [" +
1982 std::string(i_dbusObjPath) + "], error : " +
1983 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1984 return;
1985 }
1986
1987 if (!isFruReplaceableAtRuntime)
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301988 {
1989 logging::logMessage(
1990 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
1991 std::string(i_dbusObjPath));
1992 return;
1993 }
1994 }
1995 else if (dbusUtility::isBMCReady())
1996 {
Rekha Aparna40845612025-09-01 19:58:56 -05001997 uint16_t l_errCode = 0;
1998 bool isFruReplaceableAtStandby =
1999 jsonUtility::isFruReplaceableAtStandby(m_parsedJson, l_fruPath,
2000 l_errCode);
2001
2002 if (l_errCode)
2003 {
2004 logging::logMessage(
2005 "Error while checking if FRU is replaceable at standby for FRU [" +
2006 std::string(i_dbusObjPath) + "], error : " +
2007 vpdSpecificUtility::getErrCodeMsg(l_errCode));
2008 }
2009
Rekha Aparnaad0db9e2025-09-01 20:29:18 -05002010 l_errCode = 0;
2011 bool isFruReplaceableAtRuntime =
2012 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
2013 l_errCode);
2014
2015 if (l_errCode)
2016 {
2017 logging::logMessage(
2018 "Failed to check if FRU is replaceable at runtime for FRU : [" +
2019 std::string(i_dbusObjPath) + "], error : " +
2020 vpdSpecificUtility::getErrCodeMsg(l_errCode));
2021 return;
2022 }
2023
2024 if (!isFruReplaceableAtStandby && (!isFruReplaceableAtRuntime))
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302025 {
2026 logging::logMessage(
2027 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
2028 std::string(i_dbusObjPath));
2029 return;
2030 }
2031 }
2032
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002033 // Set collection Status as InProgress. Since it's an intermediate state
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302034 // D-bus set-property call is good enough to update the status.
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002035 const std::string& l_collStatusProp = "Status";
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302036
2037 if (!dbusUtility::writeDbusProperty(
2038 jsonUtility::getServiceName(m_parsedJson,
2039 std::string(i_dbusObjPath)),
2040 std::string(i_dbusObjPath), constants::vpdCollectionInterface,
2041 l_collStatusProp,
2042 types::DbusVariantType{constants::vpdCollectionInProgress}))
2043 {
2044 logging::logMessage(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002045 "Unable to set collection Status as InProgress for " +
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302046 std::string(i_dbusObjPath) +
2047 ". Continue single FRU VPD collection.");
2048 }
2049
2050 // Parse VPD
2051 types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
2052
2053 // If l_parsedVpd is pointing to std::monostate
2054 if (l_parsedVpd.index() == 0)
2055 {
2056 throw std::runtime_error(
2057 "VPD parsing failed for " + std::string(i_dbusObjPath));
2058 }
2059
2060 // Get D-bus object map from worker class
2061 types::ObjectMap l_dbusObjectMap;
2062 populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
2063
2064 if (l_dbusObjectMap.empty())
2065 {
2066 throw std::runtime_error(
2067 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
2068 std::string(i_dbusObjPath));
2069 }
2070
2071 // Call PIM's Notify method
2072 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
2073 {
2074 throw std::runtime_error(
2075 "Notify PIM failed. Single FRU VPD collection failed for " +
2076 std::string(i_dbusObjPath));
2077 }
2078 }
2079 catch (const std::exception& l_error)
2080 {
Anupama B R48f297b2025-08-13 04:29:06 -05002081 setCollectionStatusProperty(l_fruPath, constants::vpdCollectionFailed);
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302082 // TODO: Log PEL
2083 logging::logMessage(std::string(l_error.what()));
2084 }
2085}
Anupama B R24691d22025-05-21 08:14:15 -05002086
2087void Worker::setCollectionStatusProperty(
2088 const std::string& i_vpdPath, const std::string& i_value) const noexcept
2089{
2090 try
2091 {
2092 if (i_vpdPath.empty())
2093 {
2094 throw std::runtime_error(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002095 "Given path is empty. Can't set collection Status property");
Anupama B R24691d22025-05-21 08:14:15 -05002096 }
2097
2098 types::ObjectMap l_objectInterfaceMap;
2099
2100 if (m_parsedJson["frus"].contains(i_vpdPath))
2101 {
2102 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
2103 {
2104 sdbusplus::message::object_path l_fruObjectPath(
2105 l_Fru["inventoryPath"]);
2106
2107 types::PropertyMap l_propertyValueMap;
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002108 l_propertyValueMap.emplace("Status", i_value);
Anupama B R24691d22025-05-21 08:14:15 -05002109
2110 types::InterfaceMap l_interfaces;
2111 vpdSpecificUtility::insertOrMerge(
2112 l_interfaces, constants::vpdCollectionInterface,
2113 move(l_propertyValueMap));
2114
2115 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
2116 std::move(l_interfaces));
2117 }
2118 }
2119 else
2120 {
2121 // consider it as an inventory path.
2122 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
2123 {
2124 throw std::runtime_error(
2125 "Invalid inventory path: " + i_vpdPath +
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002126 ". Can't set collection Status property");
Anupama B R24691d22025-05-21 08:14:15 -05002127 }
2128
2129 types::PropertyMap l_propertyValueMap;
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002130 l_propertyValueMap.emplace("Status", i_value);
Anupama B R24691d22025-05-21 08:14:15 -05002131
2132 types::InterfaceMap l_interfaces;
2133 vpdSpecificUtility::insertOrMerge(l_interfaces,
2134 constants::vpdCollectionInterface,
2135 move(l_propertyValueMap));
2136
2137 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
2138 }
2139
2140 // Notify PIM
2141 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
2142 {
2143 throw DbusException(
2144 std::string(__FUNCTION__) +
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002145 "Call to PIM failed while setting collection Status property for path " +
Anupama B R24691d22025-05-21 08:14:15 -05002146 i_vpdPath);
2147 }
2148 }
2149 catch (const std::exception& l_ex)
2150 {
2151 EventLogger::createSyncPel(
2152 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
2153 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
2154 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
2155 }
2156}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05002157} // namespace vpd