blob: 2521606845e95d12ac178e27e6b29a1214f32159 [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 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001345 uint16_t l_errCode = 0;
Souvik Roy37c6bef2025-07-17 00:55:59 -05001346 std::string l_exMsg{
1347 std::string(__FUNCTION__) + " : VPD parsing failed for " +
1348 i_vpdFilePath + " due to error: " + l_ex.what()};
1349
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001350 // If post fail action is required, execute it.
1351 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
Sunny Srivastava4c164382025-01-28 03:17:33 -06001352 "postFailAction", "collection"))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001353 {
1354 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001355 "collection", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001356 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001357 l_exMsg += ". Post fail action also failed. Error : " +
1358 vpdSpecificUtility::getErrCodeMsg(l_errCode) +
1359 " Aborting collection for this FRU.";
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001360 }
1361 }
1362
Souvik Roy37c6bef2025-07-17 00:55:59 -05001363 if (typeid(l_ex) == typeid(DataException))
1364 {
1365 throw DataException(l_exMsg);
1366 }
1367 else if (typeid(l_ex) == typeid(EccException))
1368 {
1369 throw EccException(l_exMsg);
1370 }
1371 throw std::runtime_error(l_exMsg);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001372 }
1373}
1374
Patrick Williams43fedab2025-02-03 14:28:05 -05001375std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1376 const std::string& i_vpdFilePath)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001377{
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001378 std::string l_inventoryPath{};
1379
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001380 try
1381 {
1382 m_semaphore.acquire();
1383
1384 // Thread launched.
1385 m_mutex.lock();
1386 m_activeCollectionThreadCount++;
1387 m_mutex.unlock();
1388
Rekha Aparna017567a2025-08-13 02:07:06 -05001389 uint16_t l_errCode = 0;
1390
1391 // Set CollectionStatus as InProgress. Since it's an intermediate state
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001392 // D-bus set-property call is good enough to update the status.
RekhaAparna011ef21002025-02-18 23:47:36 -06001393 l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
Rekha Aparna017567a2025-08-13 02:07:06 -05001394 m_parsedJson, i_vpdFilePath, l_errCode);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001395
RekhaAparna011ef21002025-02-18 23:47:36 -06001396 if (!l_inventoryPath.empty())
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001397 {
Sunny Srivastava995e1c22025-08-28 03:13:00 -05001398 // save time stamp under PIM.
1399 vpdSpecificUtility::saveTimeStampInPim(l_inventoryPath,
1400 "StartTime");
1401
RekhaAparna01c532b182025-02-19 19:56:49 -06001402 if (!dbusUtility::writeDbusProperty(
RekhaAparna011ef21002025-02-18 23:47:36 -06001403 jsonUtility::getServiceName(m_parsedJson, l_inventoryPath),
1404 l_inventoryPath, constants::vpdCollectionInterface,
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001405 "Status",
RekhaAparna01c532b182025-02-19 19:56:49 -06001406 types::DbusVariantType{constants::vpdCollectionInProgress}))
RekhaAparna011ef21002025-02-18 23:47:36 -06001407 {
1408 logging::logMessage(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001409 "Unable to set collection Status as InProgress for " +
RekhaAparna01c532b182025-02-19 19:56:49 -06001410 i_vpdFilePath + ". Error : " + "DBus write failed");
RekhaAparna011ef21002025-02-18 23:47:36 -06001411 }
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001412 }
Rekha Aparna017567a2025-08-13 02:07:06 -05001413 else if (l_errCode)
1414 {
1415 logging::logMessage(
1416 "Failed to get inventory path for FRU [" + i_vpdFilePath +
1417 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
1418 }
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001419
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001420 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301421 if (!std::holds_alternative<std::monostate>(parsedVpdMap))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001422 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301423 types::ObjectMap objectInterfaceMap;
1424 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1425
Sunny Srivastava995e1c22025-08-28 03:13:00 -05001426 // save end time stamp under PIM.
1427 vpdSpecificUtility::saveTimeStampInPim(l_inventoryPath,
1428 "CompletedTime");
1429
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301430 // Notify PIM
1431 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1432 {
1433 throw std::runtime_error(
1434 std::string(__FUNCTION__) +
1435 "Call to PIM failed while publishing VPD.");
1436 }
1437 }
1438 else
1439 {
1440 logging::logMessage("Empty parsedVpdMap recieved for path [" +
1441 i_vpdFilePath + "]. Check PEL for reason.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001442 }
1443 }
1444 catch (const std::exception& ex)
1445 {
Anupama B R24691d22025-05-21 08:14:15 -05001446 setCollectionStatusProperty(i_vpdFilePath,
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001447 constants::vpdCollectionFailed);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001448
Sunny Srivastava995e1c22025-08-28 03:13:00 -05001449 // save end time stamp under PIM.
1450 vpdSpecificUtility::saveTimeStampInPim(l_inventoryPath,
1451 "CompletedTime");
1452
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001453 // handle all the exceptions internally. Return only true/false
1454 // based on status of execution.
1455 if (typeid(ex) == std::type_index(typeid(DataException)))
1456 {
Rekha Aparna017567a2025-08-13 02:07:06 -05001457 uint16_t l_errCode = 0;
Sunny Srivastava78c91072025-02-05 14:09:50 +05301458 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1459 // logging error for these cases.
1460 if (vpdSpecificUtility::isPass1Planar())
1461 {
Rekha Aparna017567a2025-08-13 02:07:06 -05001462 std::string l_invPath =
1463 jsonUtility::getInventoryObjPathFromJson(
1464 m_parsedJson, i_vpdFilePath, l_errCode);
1465
1466 if (l_errCode != 0)
1467 {
1468 logging::logMessage(
1469 "Failed to get inventory object path from JSON for FRU [" +
1470 i_vpdFilePath + "], error: " +
1471 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1472 }
1473
RekhaAparna011ef21002025-02-18 23:47:36 -06001474 const std::string& l_invPathLeafValue =
Rekha Aparna017567a2025-08-13 02:07:06 -05001475 sdbusplus::message::object_path(l_invPath).filename();
Sunny Srivastava78c91072025-02-05 14:09:50 +05301476
RekhaAparna011ef21002025-02-18 23:47:36 -06001477 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1478 std::string::npos))
Sunny Srivastava78c91072025-02-05 14:09:50 +05301479 {
1480 // skip logging any PEL for PCIe cards on pass 1 planar.
1481 return std::make_tuple(false, i_vpdFilePath);
1482 }
1483 }
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301484 }
Sunny Srivastava78c91072025-02-05 14:09:50 +05301485
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301486 EventLogger::createSyncPel(
Souvik Roy37c6bef2025-07-17 00:55:59 -05001487 EventLogger::getErrorType(ex),
1488 (typeid(ex) == typeid(DataException)) ||
1489 (typeid(ex) == typeid(EccException))
1490 ? types::SeverityType::Warning
1491 : types::SeverityType::Informational,
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301492 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1493 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001494
1495 // TODO: Figure out a way to clear data in case of any failure at
1496 // runtime.
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301497
1498 // set present property to false for any error case. In future this will
1499 // be replaced by presence logic.
Souvik Roy6a9553c2025-02-07 01:16:32 -06001500 // Update Present property for this FRU only if we handle Present
1501 // property for the FRU.
1502 if (isPresentPropertyHandlingRequired(
1503 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1504 {
1505 setPresentProperty(i_vpdFilePath, false);
1506 }
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301507
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001508 m_semaphore.release();
1509 return std::make_tuple(false, i_vpdFilePath);
1510 }
1511 m_semaphore.release();
1512 return std::make_tuple(true, i_vpdFilePath);
1513}
1514
Sunny Srivastava61611752025-02-04 00:29:33 -06001515bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1516{
1517 if (i_vpdFilePath.empty())
1518 {
1519 return true;
1520 }
1521
1522 // skip processing of system VPD again as it has been already collected.
1523 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1524 {
1525 return true;
1526 }
1527
1528 if (dbusUtility::isChassisPowerOn())
1529 {
1530 // If chassis is powered on, skip collecting FRUs which are
1531 // powerOffOnly.
1532 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath))
1533 {
1534 return true;
1535 }
1536
Rekha Aparna017567a2025-08-13 02:07:06 -05001537 uint16_t l_errCode = 0;
1538 std::string l_invPath = jsonUtility::getInventoryObjPathFromJson(
1539 m_parsedJson, i_vpdFilePath, l_errCode);
1540
1541 if (l_errCode)
1542 {
1543 logging::logMessage(
1544 "Failed to get inventory path from JSON for FRU [" +
1545 i_vpdFilePath +
1546 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
1547
1548 return false;
1549 }
1550
Sunny Srivastava61611752025-02-04 00:29:33 -06001551 const std::string& l_invPathLeafValue =
Rekha Aparna017567a2025-08-13 02:07:06 -05001552 sdbusplus::message::object_path(l_invPath).filename();
Sunny Srivastava61611752025-02-04 00:29:33 -06001553
1554 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1555 {
1556 return true;
1557 }
1558 }
1559
1560 return false;
1561}
1562
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001563void Worker::collectFrusFromJson()
1564{
1565 // A parsed JSON file should be present to pick FRUs EEPROM paths
1566 if (m_parsedJson.empty())
1567 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301568 throw JsonException(
1569 std::string(__FUNCTION__) +
1570 ": Config JSON is mandatory for processing of FRUs through this API.",
1571 m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001572 }
1573
1574 const nlohmann::json& listOfFrus =
1575 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1576
1577 for (const auto& itemFRUS : listOfFrus.items())
1578 {
1579 const std::string& vpdFilePath = itemFRUS.key();
1580
Sunny Srivastava61611752025-02-04 00:29:33 -06001581 if (skipPathForCollection(vpdFilePath))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001582 {
1583 continue;
1584 }
1585
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001586 try
1587 {
1588 std::thread{[vpdFilePath, this]() {
1589 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001590
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001591 m_mutex.lock();
1592 m_activeCollectionThreadCount--;
1593 m_mutex.unlock();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001594
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001595 if (!m_activeCollectionThreadCount)
1596 {
1597 m_isAllFruCollected = true;
1598 }
1599 }}.detach();
1600 }
1601 catch (const std::exception& l_ex)
1602 {
1603 // add vpdFilePath(EEPROM path) to failed list
1604 m_failedEepromPaths.push_front(vpdFilePath);
1605 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001606 }
1607}
1608
1609// ToDo: Move the API under IBM_SYSTEM
1610void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1611{
1612 try
1613 {
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001614 uint16_t l_errCode = 0;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001615 std::string l_backupAndRestoreCfgFilePath =
1616 m_parsedJson.value("backupRestoreConfigPath", "");
1617
1618 nlohmann::json l_backupAndRestoreCfgJsonObj =
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001619 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath,
1620 l_errCode);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001621
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001622 if (l_errCode)
RekhaAparna011ef21002025-02-18 23:47:36 -06001623 {
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001624 throw JsonException(
1625 "JSON parsing failed for file [ " +
1626 l_backupAndRestoreCfgFilePath + " ], error : " +
1627 vpdSpecificUtility::getErrCodeMsg(l_errCode),
1628 l_backupAndRestoreCfgFilePath);
RekhaAparna011ef21002025-02-18 23:47:36 -06001629 }
1630
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001631 // check if either of "source" or "destination" has inventory path.
1632 // this indicates that this sytem has System VPD on hardware
1633 // and other copy on D-Bus (BMC cache).
1634 if (!l_backupAndRestoreCfgJsonObj.empty() &&
1635 ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1636 l_backupAndRestoreCfgJsonObj["source"].contains(
1637 "inventoryPath")) ||
1638 (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1639 l_backupAndRestoreCfgJsonObj["destination"].contains(
1640 "inventoryPath"))))
1641 {
1642 BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1643 auto [l_srcVpdVariant,
1644 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1645
1646 // ToDo: Revisit is this check is required or not.
1647 if (auto l_srcVpdMap =
1648 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1649 l_srcVpdMap && !(*l_srcVpdMap).empty())
1650 {
1651 io_srcVpdMap = std::move(l_srcVpdVariant);
1652 }
1653 }
1654 }
1655 catch (const std::exception& l_ex)
1656 {
1657 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301658 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301659 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001660 std::string(
1661 "Exception caught while backup and restore VPD keyword's.") +
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301662 EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001663 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1664 }
1665}
1666
1667void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1668{
1669 if (i_dbusObjPath.empty())
1670 {
1671 throw std::runtime_error("Given DBus object path is empty.");
1672 }
1673
1674 const std::string& l_fruPath =
1675 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1676
1677 try
1678 {
1679 auto l_presentPropValue = dbusUtility::readDbusProperty(
1680 constants::pimServiceName, i_dbusObjPath,
1681 constants::inventoryItemInf, "Present");
1682
1683 if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1684 {
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001685 uint16_t l_errCode = 0;
Souvik Roye9120152025-07-02 08:24:38 -05001686 // check if FRU's Present property is handled by vpd-manager
1687 const auto& l_isFruPresenceHandled =
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001688 jsonUtility::isFruPresenceHandled(m_parsedJson, l_fruPath,
1689 l_errCode);
1690
1691 if (l_errCode)
1692 {
1693 throw std::runtime_error(
1694 "Failed to check if FRU's presence is handled, reason: " +
1695 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1696 }
Souvik Roye9120152025-07-02 08:24:38 -05001697
1698 if (!(*l_value) && l_isFruPresenceHandled)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001699 {
1700 throw std::runtime_error("Given FRU is not present");
1701 }
Souvik Roye9120152025-07-02 08:24:38 -05001702 else if (*l_value && !l_isFruPresenceHandled)
1703 {
1704 throw std::runtime_error(
1705 "Given FRU is present and its presence is not handled by vpd-manager.");
1706 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001707 else
1708 {
1709 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1710 "preAction", "deletion"))
1711 {
1712 if (!processPreAction(l_fruPath, "deletion"))
1713 {
1714 throw std::runtime_error("Pre action failed");
1715 }
1716 }
1717
1718 std::vector<std::string> l_interfaceList{
1719 constants::operationalStatusInf};
1720
1721 types::MapperGetSubTree l_subTreeMap =
1722 dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1723 l_interfaceList);
1724
1725 types::ObjectMap l_objectMap;
1726
1727 // Updates VPD specific interfaces property value under PIM for
1728 // sub FRUs.
1729 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1730 l_subTreeMap)
1731 {
1732 types::InterfaceMap l_interfaceMap;
1733 vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1734 l_interfaceMap);
1735 l_objectMap.emplace(l_objectPath,
1736 std::move(l_interfaceMap));
1737 }
1738
1739 types::InterfaceMap l_interfaceMap;
1740 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1741 l_interfaceMap);
1742
1743 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1744
1745 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1746 {
1747 throw std::runtime_error("Call to PIM failed.");
1748 }
1749
1750 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1751 "postAction", "deletion"))
1752 {
1753 if (!processPostAction(l_fruPath, "deletion"))
1754 {
1755 throw std::runtime_error("Post action failed");
1756 }
1757 }
1758 }
1759 }
1760 else
1761 {
1762 logging::logMessage(
1763 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1764 "] as unable to read present property");
1765 return;
1766 }
1767
1768 logging::logMessage(
1769 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1770 }
1771 catch (const std::exception& l_ex)
1772 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001773 uint16_t l_errCode = 0;
1774 std::string l_errMsg =
1775 "Failed to delete VPD for FRU : " + i_dbusObjPath +
1776 " error: " + std::string(l_ex.what());
1777
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001778 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1779 "postFailAction", "deletion"))
1780 {
1781 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001782 "deletion", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001783 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001784 l_errMsg += ". Post fail action also failed, error : " +
1785 vpdSpecificUtility::getErrCodeMsg(l_errCode);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001786 }
1787 }
1788
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001789 logging::logMessage(l_errMsg);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001790 }
1791}
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301792
1793void Worker::setPresentProperty(const std::string& i_vpdPath,
1794 const bool& i_value)
1795{
1796 try
1797 {
1798 if (i_vpdPath.empty())
1799 {
1800 throw std::runtime_error(
1801 "Path is empty. Can't set present property");
1802 }
1803
1804 types::ObjectMap l_objectInterfaceMap;
1805
1806 // If the given path is EEPROM path.
1807 if (m_parsedJson["frus"].contains(i_vpdPath))
1808 {
1809 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1810 {
1811 sdbusplus::message::object_path l_fruObjectPath(
1812 l_Fru["inventoryPath"]);
1813
1814 types::PropertyMap l_propertyValueMap;
1815 l_propertyValueMap.emplace("Present", i_value);
1816
1817 types::InterfaceMap l_interfaces;
1818 vpdSpecificUtility::insertOrMerge(l_interfaces,
1819 constants::inventoryItemInf,
1820 move(l_propertyValueMap));
1821
1822 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1823 std::move(l_interfaces));
1824 }
1825 }
1826 else
1827 {
1828 // consider it as an inventory path.
1829 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1830 {
1831 throw std::runtime_error(
1832 "Invalid inventory path: " + i_vpdPath);
1833 }
1834
1835 types::PropertyMap l_propertyValueMap;
1836 l_propertyValueMap.emplace("Present", i_value);
1837
1838 types::InterfaceMap l_interfaces;
1839 vpdSpecificUtility::insertOrMerge(l_interfaces,
1840 constants::inventoryItemInf,
1841 move(l_propertyValueMap));
1842
1843 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1844 }
1845
1846 // Notify PIM
1847 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1848 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301849 throw DbusException(
1850 std::string(__FUNCTION__) +
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301851 "Call to PIM failed while setting present property for path " +
1852 i_vpdPath);
1853 }
1854 }
1855 catch (const std::exception& l_ex)
1856 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301857 EventLogger::createSyncPel(
1858 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1859 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1860 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301861 }
1862}
1863
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301864void Worker::performVpdRecollection()
1865{
1866 try
1867 {
1868 // Check if system config JSON is present
1869 if (m_parsedJson.empty())
1870 {
1871 throw std::runtime_error(
1872 "System config json object is empty, can't process recollection.");
1873 }
1874
1875 const auto& l_frusReplaceableAtStandby =
1876 jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson);
1877
1878 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1879 {
1880 // ToDo: Add some logic/trace to know the flow to
1881 // collectSingleFruVpd has been directed via
1882 // performVpdRecollection.
1883 collectSingleFruVpd(l_fruInventoryPath);
1884 }
1885 return;
1886 }
1887
1888 catch (const std::exception& l_ex)
1889 {
1890 // TODO Log PEL
1891 logging::logMessage(
1892 "VPD recollection failed with error: " + std::string(l_ex.what()));
1893 }
1894}
1895
1896void Worker::collectSingleFruVpd(
1897 const sdbusplus::message::object_path& i_dbusObjPath)
1898{
Anupama B R48f297b2025-08-13 04:29:06 -05001899 std::string l_fruPath{};
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301900 try
1901 {
1902 // Check if system config JSON is present
1903 if (m_parsedJson.empty())
1904 {
1905 logging::logMessage(
1906 "System config JSON object not present. Single FRU VPD collection is not performed for " +
1907 std::string(i_dbusObjPath));
1908 return;
1909 }
1910
1911 // Get FRU path for the given D-bus object path from JSON
Anupama B R48f297b2025-08-13 04:29:06 -05001912 l_fruPath =
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301913 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1914
1915 if (l_fruPath.empty())
1916 {
1917 logging::logMessage(
1918 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
1919 std::string(i_dbusObjPath));
1920 return;
1921 }
1922
1923 // Check if host is up and running
1924 if (dbusUtility::isHostRunning())
1925 {
1926 if (!jsonUtility::isFruReplaceableAtRuntime(m_parsedJson,
1927 l_fruPath))
1928 {
1929 logging::logMessage(
1930 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
1931 std::string(i_dbusObjPath));
1932 return;
1933 }
1934 }
1935 else if (dbusUtility::isBMCReady())
1936 {
Rekha Aparna40845612025-09-01 19:58:56 -05001937 uint16_t l_errCode = 0;
1938 bool isFruReplaceableAtStandby =
1939 jsonUtility::isFruReplaceableAtStandby(m_parsedJson, l_fruPath,
1940 l_errCode);
1941
1942 if (l_errCode)
1943 {
1944 logging::logMessage(
1945 "Error while checking if FRU is replaceable at standby for FRU [" +
1946 std::string(i_dbusObjPath) + "], error : " +
1947 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1948 }
1949
1950 if (!isFruReplaceableAtStandby &&
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301951 (!jsonUtility::isFruReplaceableAtRuntime(m_parsedJson,
1952 l_fruPath)))
1953 {
1954 logging::logMessage(
1955 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
1956 std::string(i_dbusObjPath));
1957 return;
1958 }
1959 }
1960
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001961 // Set collection Status as InProgress. Since it's an intermediate state
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301962 // D-bus set-property call is good enough to update the status.
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001963 const std::string& l_collStatusProp = "Status";
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301964
1965 if (!dbusUtility::writeDbusProperty(
1966 jsonUtility::getServiceName(m_parsedJson,
1967 std::string(i_dbusObjPath)),
1968 std::string(i_dbusObjPath), constants::vpdCollectionInterface,
1969 l_collStatusProp,
1970 types::DbusVariantType{constants::vpdCollectionInProgress}))
1971 {
1972 logging::logMessage(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001973 "Unable to set collection Status as InProgress for " +
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301974 std::string(i_dbusObjPath) +
1975 ". Continue single FRU VPD collection.");
1976 }
1977
1978 // Parse VPD
1979 types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
1980
1981 // If l_parsedVpd is pointing to std::monostate
1982 if (l_parsedVpd.index() == 0)
1983 {
1984 throw std::runtime_error(
1985 "VPD parsing failed for " + std::string(i_dbusObjPath));
1986 }
1987
1988 // Get D-bus object map from worker class
1989 types::ObjectMap l_dbusObjectMap;
1990 populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
1991
1992 if (l_dbusObjectMap.empty())
1993 {
1994 throw std::runtime_error(
1995 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
1996 std::string(i_dbusObjPath));
1997 }
1998
1999 // Call PIM's Notify method
2000 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
2001 {
2002 throw std::runtime_error(
2003 "Notify PIM failed. Single FRU VPD collection failed for " +
2004 std::string(i_dbusObjPath));
2005 }
2006 }
2007 catch (const std::exception& l_error)
2008 {
Anupama B R48f297b2025-08-13 04:29:06 -05002009 setCollectionStatusProperty(l_fruPath, constants::vpdCollectionFailed);
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302010 // TODO: Log PEL
2011 logging::logMessage(std::string(l_error.what()));
2012 }
2013}
Anupama B R24691d22025-05-21 08:14:15 -05002014
2015void Worker::setCollectionStatusProperty(
2016 const std::string& i_vpdPath, const std::string& i_value) const noexcept
2017{
2018 try
2019 {
2020 if (i_vpdPath.empty())
2021 {
2022 throw std::runtime_error(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002023 "Given path is empty. Can't set collection Status property");
Anupama B R24691d22025-05-21 08:14:15 -05002024 }
2025
2026 types::ObjectMap l_objectInterfaceMap;
2027
2028 if (m_parsedJson["frus"].contains(i_vpdPath))
2029 {
2030 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
2031 {
2032 sdbusplus::message::object_path l_fruObjectPath(
2033 l_Fru["inventoryPath"]);
2034
2035 types::PropertyMap l_propertyValueMap;
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002036 l_propertyValueMap.emplace("Status", i_value);
Anupama B R24691d22025-05-21 08:14:15 -05002037
2038 types::InterfaceMap l_interfaces;
2039 vpdSpecificUtility::insertOrMerge(
2040 l_interfaces, constants::vpdCollectionInterface,
2041 move(l_propertyValueMap));
2042
2043 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
2044 std::move(l_interfaces));
2045 }
2046 }
2047 else
2048 {
2049 // consider it as an inventory path.
2050 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
2051 {
2052 throw std::runtime_error(
2053 "Invalid inventory path: " + i_vpdPath +
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002054 ". Can't set collection Status property");
Anupama B R24691d22025-05-21 08:14:15 -05002055 }
2056
2057 types::PropertyMap l_propertyValueMap;
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002058 l_propertyValueMap.emplace("Status", i_value);
Anupama B R24691d22025-05-21 08:14:15 -05002059
2060 types::InterfaceMap l_interfaces;
2061 vpdSpecificUtility::insertOrMerge(l_interfaces,
2062 constants::vpdCollectionInterface,
2063 move(l_propertyValueMap));
2064
2065 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
2066 }
2067
2068 // Notify PIM
2069 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
2070 {
2071 throw DbusException(
2072 std::string(__FUNCTION__) +
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002073 "Call to PIM failed while setting collection Status property for path " +
Anupama B R24691d22025-05-21 08:14:15 -05002074 i_vpdPath);
2075 }
2076 }
2077 catch (const std::exception& l_ex)
2078 {
2079 EventLogger::createSyncPel(
2080 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
2081 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
2082 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
2083 }
2084}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05002085} // namespace vpd