blob: 7a4ce00f76eb15e994c8e6640d300f535630cdd9 [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{
Anupama B R4c65fcd2025-09-01 08:09:00 -0500362 setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH,
363 constants::vpdCollectionInProgress);
364
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500365 // JSON is madatory for processing of this API.
366 if (m_parsedJson.empty())
367 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530368 throw JsonException("System config JSON is empty", m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500369 }
370
371 types::VPDMapVariant parsedVpdMap;
372 fillVPDMap(SYSTEM_VPD_FILE_PATH, parsedVpdMap);
373
374 // Implies it is default JSON.
375 std::string systemJson{JSON_ABSOLUTE_PATH_PREFIX};
376
377 // ToDo: Need to check if INVENTORY_JSON_SYM_LINK pointing to correct system
378 // This is required to support movement from rainier to Blue Ridge on the
379 // fly.
380
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530381 getSystemJson(systemJson, parsedVpdMap);
382
383 if (!systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500384 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530385 throw DataException(
386 "No system JSON found corresponding to IM read from VPD.");
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530387 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500388
Rekha Aparnaca9a0862025-08-29 04:08:33 -0500389 uint16_t l_errCode = 0;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500390
Rekha Aparnaca9a0862025-08-29 04:08:33 -0500391 // re-parse the JSON once appropriate JSON has been selected.
392 m_parsedJson = jsonUtility::getParsedJson(systemJson, l_errCode);
393
394 if (l_errCode)
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530395 {
Rekha Aparnaca9a0862025-08-29 04:08:33 -0500396 throw(JsonException(
397 "JSON parsing failed for file [ " + systemJson +
398 " ], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode),
399 systemJson));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500400 }
401
402 std::string devTreeFromJson;
403 if (m_parsedJson.contains("devTree"))
404 {
405 devTreeFromJson = m_parsedJson["devTree"];
406
407 if (devTreeFromJson.empty())
408 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530409 EventLogger::createSyncPel(
410 types::ErrorType::JsonFailure, types::SeverityType::Error,
411 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500412 "Mandatory value for device tree missing from JSON[" +
Sunny Srivastava043955d2025-01-21 18:04:49 +0530413 systemJson + "]",
414 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500415 }
416 }
417
418 auto fitConfigVal = readFitConfigValue();
419
420 if (devTreeFromJson.empty() ||
421 fitConfigVal.find(devTreeFromJson) != std::string::npos)
422 { // Skipping setting device tree as either devtree info is missing from
423 // Json or it is rightly set.
424
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530425 setJsonSymbolicLink(systemJson);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500426
427 if (isSystemVPDOnDBus() &&
428 jsonUtility::isBackupAndRestoreRequired(m_parsedJson))
429 {
430 performBackupAndRestore(parsedVpdMap);
431 }
432
433 // proceed to publish system VPD.
434 publishSystemVPD(parsedVpdMap);
Anupama B R4c65fcd2025-09-01 08:09:00 -0500435 setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH,
436 constants::vpdCollectionCompleted);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500437 return;
438 }
439
440 setEnvAndReboot("fitconfig", devTreeFromJson);
441 exit(EXIT_SUCCESS);
442}
443
444void Worker::populateIPZVPDpropertyMap(
445 types::InterfaceMap& interfacePropMap,
446 const types::IPZKwdValueMap& keyordValueMap,
447 const std::string& interfaceName)
448{
449 types::PropertyMap propertyValueMap;
450 for (const auto& kwdVal : keyordValueMap)
451 {
452 auto kwd = kwdVal.first;
453
454 if (kwd[0] == '#')
455 {
456 kwd = std::string("PD_") + kwd[1];
457 }
458 else if (isdigit(kwd[0]))
459 {
460 kwd = std::string("N_") + kwd;
461 }
462
463 types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end());
464 propertyValueMap.emplace(move(kwd), move(value));
465 }
466
467 if (!propertyValueMap.empty())
468 {
469 interfacePropMap.emplace(interfaceName, propertyValueMap);
470 }
471}
472
473void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
474 types::InterfaceMap& interfaceMap)
475{
476 for (const auto& kwdValMap : keyordVPDMap)
477 {
478 types::PropertyMap propertyValueMap;
479 auto kwd = kwdValMap.first;
480
481 if (kwd[0] == '#')
482 {
483 kwd = std::string("PD_") + kwd[1];
484 }
485 else if (isdigit(kwd[0]))
486 {
487 kwd = std::string("N_") + kwd;
488 }
489
490 if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second))
491 {
492 types::BinaryVector value((*keywordValue).begin(),
493 (*keywordValue).end());
494 propertyValueMap.emplace(move(kwd), move(value));
495 }
496 else if (auto keywordValue = get_if<std::string>(&kwdValMap.second))
497 {
498 types::BinaryVector value((*keywordValue).begin(),
499 (*keywordValue).end());
500 propertyValueMap.emplace(move(kwd), move(value));
501 }
502 else if (auto keywordValue = get_if<size_t>(&kwdValMap.second))
503 {
504 if (kwd == "MemorySizeInKB")
505 {
506 types::PropertyMap memProp;
507 memProp.emplace(move(kwd), ((*keywordValue)));
508 interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm",
509 move(memProp));
510 continue;
511 }
512 else
513 {
514 logging::logMessage(
515 "Unknown Keyword =" + kwd + " found in keyword VPD map");
516 continue;
517 }
518 }
519 else
520 {
521 logging::logMessage(
522 "Unknown variant type found in keyword VPD map.");
523 continue;
524 }
525
526 if (!propertyValueMap.empty())
527 {
528 vpdSpecificUtility::insertOrMerge(
529 interfaceMap, constants::kwdVpdInf, move(propertyValueMap));
530 }
531 }
532}
533
534void Worker::populateInterfaces(const nlohmann::json& interfaceJson,
535 types::InterfaceMap& interfaceMap,
536 const types::VPDMapVariant& parsedVpdMap)
537{
538 for (const auto& interfacesPropPair : interfaceJson.items())
539 {
540 const std::string& interface = interfacesPropPair.key();
541 types::PropertyMap propertyMap;
542
543 for (const auto& propValuePair : interfacesPropPair.value().items())
544 {
545 const std::string property = propValuePair.key();
546
547 if (propValuePair.value().is_boolean())
548 {
549 propertyMap.emplace(property,
550 propValuePair.value().get<bool>());
551 }
552 else if (propValuePair.value().is_string())
553 {
554 if (property.compare("LocationCode") == 0 &&
555 interface.compare("com.ibm.ipzvpd.Location") == 0)
556 {
557 std::string value =
558 vpdSpecificUtility::getExpandedLocationCode(
559 propValuePair.value().get<std::string>(),
560 parsedVpdMap);
561 propertyMap.emplace(property, value);
562
563 auto l_locCodeProperty = propertyMap;
564 vpdSpecificUtility::insertOrMerge(
565 interfaceMap,
566 std::string(constants::xyzLocationCodeInf),
567 move(l_locCodeProperty));
568 }
569 else
570 {
571 propertyMap.emplace(
572 property, propValuePair.value().get<std::string>());
573 }
574 }
575 else if (propValuePair.value().is_array())
576 {
577 try
578 {
579 propertyMap.emplace(
580 property,
581 propValuePair.value().get<types::BinaryVector>());
582 }
583 catch (const nlohmann::detail::type_error& e)
584 {
585 std::cerr << "Type exception: " << e.what() << "\n";
586 }
587 }
588 else if (propValuePair.value().is_number())
589 {
590 // For now assume the value is a size_t. In the future it would
591 // be nice to come up with a way to get the type from the JSON.
592 propertyMap.emplace(property,
593 propValuePair.value().get<size_t>());
594 }
595 else if (propValuePair.value().is_object())
596 {
597 const std::string& record =
598 propValuePair.value().value("recordName", "");
599 const std::string& keyword =
600 propValuePair.value().value("keywordName", "");
601 const std::string& encoding =
602 propValuePair.value().value("encoding", "");
603
604 if (auto ipzVpdMap =
605 std::get_if<types::IPZVpdMap>(&parsedVpdMap))
606 {
607 if (!record.empty() && !keyword.empty() &&
608 (*ipzVpdMap).count(record) &&
609 (*ipzVpdMap).at(record).count(keyword))
610 {
611 auto encoded = vpdSpecificUtility::encodeKeyword(
612 ((*ipzVpdMap).at(record).at(keyword)), encoding);
613 propertyMap.emplace(property, encoded);
614 }
615 }
616 else if (auto kwdVpdMap =
617 std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
618 {
619 if (!keyword.empty() && (*kwdVpdMap).count(keyword))
620 {
621 if (auto kwValue = std::get_if<types::BinaryVector>(
622 &(*kwdVpdMap).at(keyword)))
623 {
624 auto encodedValue =
625 vpdSpecificUtility::encodeKeyword(
626 std::string((*kwValue).begin(),
627 (*kwValue).end()),
628 encoding);
629
630 propertyMap.emplace(property, encodedValue);
631 }
632 else if (auto kwValue = std::get_if<std::string>(
633 &(*kwdVpdMap).at(keyword)))
634 {
635 auto encodedValue =
636 vpdSpecificUtility::encodeKeyword(
637 std::string((*kwValue).begin(),
638 (*kwValue).end()),
639 encoding);
640
641 propertyMap.emplace(property, encodedValue);
642 }
643 else if (auto uintValue = std::get_if<size_t>(
644 &(*kwdVpdMap).at(keyword)))
645 {
646 propertyMap.emplace(property, *uintValue);
647 }
648 else
649 {
650 logging::logMessage(
651 "Unknown keyword found, Keywrod = " + keyword);
652 }
653 }
654 }
655 }
656 }
657 vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
658 move(propertyMap));
659 }
660}
661
662bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
663{
664 const unsigned char l_io[] = {
665 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
666 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
667
668 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
669 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
670 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
671 // IO.
672 if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
673 constants::SIZE_OF_8EQ_IN_PG) == 0)
674 {
675 return true;
676 }
677
678 // The CPU is not an IO
679 return false;
680}
681
682bool Worker::primeInventory(const std::string& i_vpdFilePath)
683{
684 if (i_vpdFilePath.empty())
685 {
686 logging::logMessage("Empty VPD file path given");
687 return false;
688 }
689
690 if (m_parsedJson.empty())
691 {
692 logging::logMessage("Empty JSON detected for " + i_vpdFilePath);
693 return false;
694 }
695 else if (!m_parsedJson["frus"].contains(i_vpdFilePath))
696 {
697 logging::logMessage("File " + i_vpdFilePath +
698 ", is not found in the system config JSON file.");
699 return false;
700 }
701
702 types::ObjectMap l_objectInterfaceMap;
703 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdFilePath])
704 {
705 types::InterfaceMap l_interfaces;
706 sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
707
708 if (l_Fru.contains("ccin"))
709 {
710 continue;
711 }
712
713 if (l_Fru.contains("noprime") && l_Fru.value("noprime", false))
714 {
715 continue;
716 }
717
Souvik Roy6a9553c2025-02-07 01:16:32 -0600718 // Reset data under PIM for this FRU only if the FRU is not synthesized
719 // and we handle it's Present property.
720 if (isPresentPropertyHandlingRequired(l_Fru))
721 {
722 // Clear data under PIM if already exists.
723 vpdSpecificUtility::resetDataUnderPIM(
724 std::string(l_Fru["inventoryPath"]), l_interfaces);
725 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500726
727 // Add extra interfaces mentioned in the Json config file
728 if (l_Fru.contains("extraInterfaces"))
729 {
730 populateInterfaces(l_Fru["extraInterfaces"], l_interfaces,
731 std::monostate{});
732 }
733
734 types::PropertyMap l_propertyValueMap;
Sunny Srivastavad159bb42025-01-09 11:13:50 +0530735
Souvik Roy6a9553c2025-02-07 01:16:32 -0600736 // Update Present property for this FRU only if we handle Present
737 // property for the FRU.
738 if (isPresentPropertyHandlingRequired(l_Fru))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500739 {
Souvik Roy6a9553c2025-02-07 01:16:32 -0600740 l_propertyValueMap.emplace("Present", false);
741
742 // TODO: Present based on file will be taken care in future.
743 // By default present is set to false for FRU at the time of
744 // priming. Once collection goes through, it will be set to true in
745 // that flow.
746 /*if (std::filesystem::exists(i_vpdFilePath))
747 {
748 l_propertyValueMap["Present"] = true;
749 }*/
750 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500751
752 vpdSpecificUtility::insertOrMerge(l_interfaces,
753 "xyz.openbmc_project.Inventory.Item",
754 move(l_propertyValueMap));
755
756 if (l_Fru.value("inherit", true) &&
757 m_parsedJson.contains("commonInterfaces"))
758 {
759 populateInterfaces(m_parsedJson["commonInterfaces"], l_interfaces,
760 std::monostate{});
761 }
762
763 processFunctionalProperty(l_Fru["inventoryPath"], l_interfaces);
764 processEnabledProperty(l_Fru["inventoryPath"], l_interfaces);
765
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -0600766 // Emplace the default state of FRU VPD collection
767 types::PropertyMap l_fruCollectionProperty = {
Anupama B R5cd1b2d2025-08-05 04:57:40 -0500768 {"Status", constants::vpdCollectionNotStarted}};
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -0600769
770 vpdSpecificUtility::insertOrMerge(l_interfaces,
771 constants::vpdCollectionInterface,
772 std::move(l_fruCollectionProperty));
773
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500774 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
775 std::move(l_interfaces));
776 }
777
778 // Notify PIM
779 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
780 {
781 logging::logMessage("Call to PIM failed for VPD file " + i_vpdFilePath);
782 return false;
783 }
784
785 return true;
786}
787
788void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
789 types::InterfaceMap& interfaces)
790{
791 // embedded property(true or false) says whether the subfru is embedded
792 // into the parent fru (or) not. VPD sets Present property only for
793 // embedded frus. If the subfru is not an embedded FRU, the subfru may
794 // or may not be physically present. Those non embedded frus will always
795 // have Present=false irrespective of its physical presence or absence.
796 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
797 // Present to true for such sub frus.
798 // Eg: ethernet port is embedded into bmc card. So set Present to true
799 // for such sub frus. Also donot populate present property for embedded
800 // subfru which is synthesized. Currently there is no subfru which are
801 // both embedded and synthesized. But still the case is handled here.
802
803 // Check if its required to handle presence for this FRU.
804 if (singleFru.value("handlePresence", true))
805 {
806 types::PropertyMap presProp;
807 presProp.emplace("Present", true);
808 vpdSpecificUtility::insertOrMerge(
809 interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp));
810 }
811}
812
813void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
814 types::InterfaceMap& interfaces,
815 const types::VPDMapVariant& parsedVpdMap)
816{
817 populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
818 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
819 {
820 if (singleFru["extraInterfaces"].contains(
821 "xyz.openbmc_project.Inventory.Item.Cpu"))
822 {
823 auto itrToRec = (*ipzVpdMap).find("CP00");
824 if (itrToRec == (*ipzVpdMap).end())
825 {
826 return;
827 }
828
Souvik Roya55fcca2025-02-19 01:33:58 -0600829 const std::string pgKeywordValue{
830 vpdSpecificUtility::getKwVal(itrToRec->second, "PG")};
831
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500832 if (!pgKeywordValue.empty())
833 {
834 if (isCPUIOGoodOnly(pgKeywordValue))
835 {
836 interfaces["xyz.openbmc_project.Inventory.Item"]
837 ["PrettyName"] = "IO Module";
838 }
839 }
Souvik Roya55fcca2025-02-19 01:33:58 -0600840 else
841 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +0530842 throw DataException(std::string(__FUNCTION__) +
843 "Failed to get value for keyword PG");
Souvik Roya55fcca2025-02-19 01:33:58 -0600844 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500845 }
846 }
847}
848
849void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
850 const types::VPDMapVariant& parsedVpdMap,
851 types::InterfaceMap& interfaces)
852{
853 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
854 {
855 for (const auto& record : singleFru["copyRecords"])
856 {
857 const std::string& recordName = record;
858 if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
859 {
860 populateIPZVPDpropertyMap(interfaces,
861 (*ipzVpdMap).at(recordName),
862 constants::ipzVpdInf + recordName);
863 }
864 }
865 }
866}
867
868void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
869 types::InterfaceMap& interfaces)
870{
871 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
872 {
873 for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
874 {
875 populateIPZVPDpropertyMap(interfaces, kwdValueMap,
876 constants::ipzVpdInf + recordName);
877 }
878 }
879 else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
880 {
881 populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
882 }
883
884 if (m_parsedJson.contains("commonInterfaces"))
885 {
886 populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
887 parsedVpdMap);
888 }
889}
890
891bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
892 const types::VPDMapVariant& parsedVpdMap)
893{
894 if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
895 {
896 auto itrToRec = (*ipzVPDMap).find("VINI");
897 if (itrToRec == (*ipzVPDMap).end())
898 {
899 return false;
900 }
901
Souvik Roya55fcca2025-02-19 01:33:58 -0600902 std::string ccinFromVpd{
903 vpdSpecificUtility::getKwVal(itrToRec->second, "CC")};
904
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500905 if (ccinFromVpd.empty())
906 {
907 return false;
908 }
909
910 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
911 ::toupper);
912
913 std::vector<std::string> ccinList;
914 for (std::string ccin : singleFru["ccin"])
915 {
916 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
917 ccinList.push_back(ccin);
918 }
919
920 if (ccinList.empty())
921 {
922 return false;
923 }
924
925 if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
926 ccinList.end())
927 {
928 return false;
929 }
930 }
931 return true;
932}
933
934void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
935 types::InterfaceMap& io_interfaces)
936{
937 if (!dbusUtility::isChassisPowerOn())
938 {
939 std::array<const char*, 1> l_operationalStatusInf = {
940 constants::operationalStatusInf};
941
942 auto mapperObjectMap = dbusUtility::getObjectMap(
943 i_inventoryObjPath, l_operationalStatusInf);
944
945 // If the object has been found. Check if it is under PIM.
946 if (mapperObjectMap.size() != 0)
947 {
948 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
949 {
950 if (l_serviceName == constants::pimServiceName)
951 {
952 // The object is already under PIM. No need to process
953 // again. Retain the old value.
954 return;
955 }
956 }
957 }
958
959 // Implies value is not there in D-Bus. Populate it with default
960 // value "true".
961 types::PropertyMap l_functionalProp;
962 l_functionalProp.emplace("Functional", true);
963 vpdSpecificUtility::insertOrMerge(io_interfaces,
964 constants::operationalStatusInf,
965 move(l_functionalProp));
966 }
967
968 // if chassis is power on. Functional property should be there on D-Bus.
969 // Don't process.
970 return;
971}
972
973void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
974 types::InterfaceMap& io_interfaces)
975{
976 if (!dbusUtility::isChassisPowerOn())
977 {
978 std::array<const char*, 1> l_enableInf = {constants::enableInf};
979
980 auto mapperObjectMap =
981 dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
982
983 // If the object has been found. Check if it is under PIM.
984 if (mapperObjectMap.size() != 0)
985 {
986 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
987 {
988 if (l_serviceName == constants::pimServiceName)
989 {
990 // The object is already under PIM. No need to process
991 // again. Retain the old value.
992 return;
993 }
994 }
995 }
996
997 // Implies value is not there in D-Bus. Populate it with default
998 // value "true".
999 types::PropertyMap l_enabledProp;
1000 l_enabledProp.emplace("Enabled", true);
1001 vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
1002 move(l_enabledProp));
1003 }
1004
1005 // if chassis is power on. Enabled property should be there on D-Bus.
1006 // Don't process.
1007 return;
1008}
1009
1010void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
1011 types::ObjectMap& objectInterfaceMap,
1012 const std::string& vpdFilePath)
1013{
1014 if (vpdFilePath.empty())
1015 {
1016 throw std::runtime_error(
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301017 std::string(__FUNCTION__) +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001018 "Invalid parameter passed to populateDbus API.");
1019 }
1020
1021 // JSON config is mandatory for processing of "if". Add "else" for any
1022 // processing without config JSON.
1023 if (!m_parsedJson.empty())
1024 {
1025 types::InterfaceMap interfaces;
1026
1027 for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
1028 {
1029 const auto& inventoryPath = aFru["inventoryPath"];
1030 sdbusplus::message::object_path fruObjectPath(inventoryPath);
1031 if (aFru.contains("ccin"))
1032 {
1033 if (!processFruWithCCIN(aFru, parsedVpdMap))
1034 {
1035 continue;
1036 }
1037 }
1038
1039 if (aFru.value("inherit", true))
1040 {
1041 processInheritFlag(parsedVpdMap, interfaces);
1042 }
1043
1044 // If specific record needs to be copied.
1045 if (aFru.contains("copyRecords"))
1046 {
1047 processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
1048 }
1049
1050 if (aFru.contains("extraInterfaces"))
1051 {
1052 // Process extra interfaces w.r.t a FRU.
1053 processExtraInterfaces(aFru, interfaces, parsedVpdMap);
1054 }
1055
1056 // Process FRUS which are embedded in the parent FRU and whose VPD
1057 // will be synthesized.
1058 if ((aFru.value("embedded", true)) &&
1059 (!aFru.value("synthesized", false)))
1060 {
1061 processEmbeddedAndSynthesizedFrus(aFru, interfaces);
1062 }
1063
1064 processFunctionalProperty(inventoryPath, interfaces);
1065 processEnabledProperty(inventoryPath, interfaces);
1066
1067 objectInterfaceMap.emplace(std::move(fruObjectPath),
1068 std::move(interfaces));
1069 }
1070 }
1071}
1072
Patrick Williams43fedab2025-02-03 14:28:05 -05001073std::string Worker::createAssetTagString(
1074 const types::VPDMapVariant& i_parsedVpdMap)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001075{
1076 std::string l_assetTag;
1077
1078 // system VPD will be in IPZ format.
1079 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
1080 {
1081 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
1082 if (l_itrToVsys != (*l_parsedVpdMap).end())
1083 {
Souvik Roya55fcca2025-02-19 01:33:58 -06001084 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal(
1085 l_itrToVsys->second, constants::kwdTM)};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001086
Souvik Roya55fcca2025-02-19 01:33:58 -06001087 if (l_tmKwdValue.empty())
1088 {
1089 throw std::runtime_error(
1090 std::string("Failed to get value for keyword [") +
1091 constants::kwdTM +
1092 std::string("] while creating Asset tag."));
1093 }
1094
1095 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal(
1096 l_itrToVsys->second, constants::kwdSE)};
1097
1098 if (l_seKwdValue.empty())
1099 {
1100 throw std::runtime_error(
1101 std::string("Failed to get value for keyword [") +
1102 constants::kwdSE +
1103 std::string("] while creating Asset tag."));
1104 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001105
1106 l_assetTag = std::string{"Server-"} + l_tmKwdValue +
1107 std::string{"-"} + l_seKwdValue;
1108 }
1109 else
1110 {
1111 throw std::runtime_error(
1112 "VSYS record not found in parsed VPD map to create Asset tag.");
1113 }
1114 }
1115 else
1116 {
1117 throw std::runtime_error(
1118 "Invalid VPD type recieved to create Asset tag.");
1119 }
1120
1121 return l_assetTag;
1122}
1123
1124void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
1125{
1126 types::ObjectMap objectInterfaceMap;
1127
1128 if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
1129 {
1130 populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
1131
1132 try
1133 {
1134 if (m_isFactoryResetDone)
1135 {
1136 const auto& l_assetTag = createAssetTagString(parsedVpdMap);
1137
1138 auto l_itrToSystemPath = objectInterfaceMap.find(
1139 sdbusplus::message::object_path(constants::systemInvPath));
1140 if (l_itrToSystemPath == objectInterfaceMap.end())
1141 {
1142 throw std::runtime_error(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301143 "Asset tag update failed. System Path not found in object map.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001144 }
1145
1146 types::PropertyMap l_assetTagProperty;
1147 l_assetTagProperty.emplace("AssetTag", l_assetTag);
1148
1149 (l_itrToSystemPath->second)
1150 .emplace(constants::assetTagInf,
1151 std::move(l_assetTagProperty));
1152 }
1153 }
1154 catch (const std::exception& l_ex)
1155 {
1156 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301157 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1158 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001159 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1160 }
1161
1162 // Notify PIM
1163 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1164 {
1165 throw std::runtime_error("Call to PIM failed for system VPD");
1166 }
1167 }
1168 else
1169 {
1170 throw DataException("Invalid format of parsed VPD map.");
1171 }
1172}
1173
1174bool Worker::processPreAction(const std::string& i_vpdFilePath,
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001175 const std::string& i_flagToProcess,
1176 uint16_t& i_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001177{
1178 if (i_vpdFilePath.empty() || i_flagToProcess.empty())
1179 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001180 i_errCode = error_code::INVALID_INPUT_PARAMETER;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001181 return false;
1182 }
1183
1184 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001185 i_vpdFilePath, i_flagToProcess,
1186 i_errCode)) &&
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001187 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
1188 {
1189 // TODO: Need a way to delete inventory object from Dbus and persisted
1190 // data section in case any FRU is not present or there is any
1191 // problem in collecting it. Once it has been deleted, it can be
1192 // re-created in the flow of priming the inventory. This needs to be
1193 // done either here or in the exception section of "parseAndPublishVPD"
1194 // API. Any failure in the process of collecting FRU will land up in the
1195 // excpetion of "parseAndPublishVPD".
1196
1197 // If the FRU is not there, clear the VINI/CCIN data.
1198 // Enity manager probes for this keyword to look for this
1199 // FRU, now if the data is persistent on BMC and FRU is
1200 // removed this can lead to ambiguity. Hence clearing this
1201 // Keyword if FRU is absent.
1202 const auto& inventoryPath =
1203 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
1204 "");
1205
1206 if (!inventoryPath.empty())
1207 {
1208 types::ObjectMap l_pimObjMap{
1209 {inventoryPath,
1210 {{constants::kwdVpdInf,
1211 {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
1212
1213 if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
1214 {
1215 logging::logMessage(
1216 "Call to PIM failed for file " + i_vpdFilePath);
1217 }
1218 }
1219 else
1220 {
1221 logging::logMessage(
1222 "Inventory path is empty in Json for file " + i_vpdFilePath);
1223 }
1224
1225 return false;
1226 }
1227 return true;
1228}
1229
1230bool Worker::processPostAction(
1231 const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
1232 const std::optional<types::VPDMapVariant> i_parsedVpd)
1233{
1234 if (i_vpdFruPath.empty() || i_flagToProcess.empty())
1235 {
1236 logging::logMessage(
1237 "Invalid input parameter. Abort processing post action");
1238 return false;
1239 }
1240
1241 // Check if post action tag is to be triggered in the flow of collection
1242 // based on some CCIN value?
1243 if (m_parsedJson["frus"][i_vpdFruPath]
1244 .at(0)["postAction"][i_flagToProcess]
1245 .contains("ccin"))
1246 {
1247 if (!i_parsedVpd.has_value())
1248 {
1249 logging::logMessage("Empty VPD Map");
1250 return false;
1251 }
1252
1253 // CCIN match is required to process post action for this FRU as it
1254 // contains the flag.
1255 if (!vpdSpecificUtility::findCcinInVpd(
1256 m_parsedJson["frus"][i_vpdFruPath].at(
1257 0)["postAction"]["collection"],
1258 i_parsedVpd.value()))
1259 {
1260 // If CCIN is not found, implies post action processing is not
1261 // required for this FRU. Let the flow continue.
1262 return true;
1263 }
1264 }
1265
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001266 uint16_t l_errCode = 0;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001267 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001268 i_vpdFruPath, i_flagToProcess,
1269 l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001270 {
1271 logging::logMessage(
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001272 "Execution of post action failed for path: " + i_vpdFruPath +
1273 " . Reason: " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001274
1275 // If post action was required and failed only in that case return
1276 // false. In all other case post action is considered passed.
1277 return false;
1278 }
1279
1280 return true;
1281}
1282
1283types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
1284{
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001285 try
1286 {
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001287 uint16_t l_errCode = 0;
1288
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301289 if (i_vpdFilePath.empty())
1290 {
1291 throw std::runtime_error(
1292 std::string(__FUNCTION__) +
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301293 " Empty VPD file path passed. Abort processing");
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301294 }
1295
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301296 bool isPreActionRequired = false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001297 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001298 "preAction", "collection", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001299 {
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001300 l_errCode = 0;
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301301 isPreActionRequired = true;
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001302 if (!processPreAction(i_vpdFilePath, "collection", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001303 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001304 if (l_errCode == error_code::DEVICE_NOT_PRESENT)
1305 {
1306 logging::logMessage(
1307 vpdSpecificUtility::getErrCodeMsg(l_errCode) +
1308 i_vpdFilePath);
1309 // Presence pin has been read successfully and has been read
1310 // as false, so this is not a failure case, hence returning
1311 // empty variant so that pre action is not marked as failed.
1312 return types::VPDMapVariant{};
1313 }
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301314 throw std::runtime_error(
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001315 std::string(__FUNCTION__) +
1316 " Pre-Action failed with error: " +
1317 vpdSpecificUtility::getErrCodeMsg(l_errCode));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001318 }
1319 }
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001320 else if (l_errCode)
1321 {
1322 logging::logMessage(
1323 "Failed to check if pre action required for FRU [" +
1324 i_vpdFilePath +
1325 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
1326 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001327
1328 if (!std::filesystem::exists(i_vpdFilePath))
1329 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301330 if (isPreActionRequired)
1331 {
1332 throw std::runtime_error(
1333 std::string(__FUNCTION__) + " Could not find file path " +
1334 i_vpdFilePath + "Skipping parser trigger for the EEPROM");
1335 }
1336 return types::VPDMapVariant{};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001337 }
1338
1339 std::shared_ptr<Parser> vpdParser =
1340 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
1341
1342 types::VPDMapVariant l_parsedVpd = vpdParser->parse();
1343
1344 // Before returning, as collection is over, check if FRU qualifies for
1345 // any post action in the flow of collection.
1346 // Note: Don't change the order, post action needs to be processed only
1347 // after collection for FRU is successfully done.
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001348 l_errCode = 0;
1349
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001350 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001351 "postAction", "collection",
1352 l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001353 {
1354 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
1355 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301356 // Post action was required but failed while executing.
1357 // Behaviour can be undefined.
1358 EventLogger::createSyncPel(
1359 types::ErrorType::InternalFailure,
1360 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
1361 std::string("Required post action failed for path [" +
1362 i_vpdFilePath + "]"),
1363 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001364 }
1365 }
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001366 else if (l_errCode)
1367 {
1368 logging::logMessage(
1369 "Error while checking if post action required for FRU [" +
1370 i_vpdFilePath +
1371 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
1372 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001373
1374 return l_parsedVpd;
1375 }
1376 catch (std::exception& l_ex)
1377 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001378 uint16_t l_errCode = 0;
Souvik Roy37c6bef2025-07-17 00:55:59 -05001379 std::string l_exMsg{
1380 std::string(__FUNCTION__) + " : VPD parsing failed for " +
1381 i_vpdFilePath + " due to error: " + l_ex.what()};
1382
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001383 // If post fail action is required, execute it.
1384 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001385 "postFailAction", "collection",
1386 l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001387 {
1388 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001389 "collection", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001390 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001391 l_exMsg += ". Post fail action also failed. Error : " +
1392 vpdSpecificUtility::getErrCodeMsg(l_errCode) +
1393 " Aborting collection for this FRU.";
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001394 }
1395 }
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001396 else if (l_errCode)
1397 {
1398 l_exMsg +=
1399 ". Failed to check if post fail action required, error : " +
1400 vpdSpecificUtility::getErrCodeMsg(l_errCode);
1401 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001402
Souvik Roy37c6bef2025-07-17 00:55:59 -05001403 if (typeid(l_ex) == typeid(DataException))
1404 {
1405 throw DataException(l_exMsg);
1406 }
1407 else if (typeid(l_ex) == typeid(EccException))
1408 {
1409 throw EccException(l_exMsg);
1410 }
1411 throw std::runtime_error(l_exMsg);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001412 }
1413}
1414
Patrick Williams43fedab2025-02-03 14:28:05 -05001415std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1416 const std::string& i_vpdFilePath)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001417{
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001418 std::string l_inventoryPath{};
1419
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001420 try
1421 {
1422 m_semaphore.acquire();
1423
1424 // Thread launched.
1425 m_mutex.lock();
1426 m_activeCollectionThreadCount++;
1427 m_mutex.unlock();
1428
Anupama B R4c65fcd2025-09-01 08:09:00 -05001429 setCollectionStatusProperty(i_vpdFilePath,
1430 constants::vpdCollectionInProgress);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001431
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001432 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301433 if (!std::holds_alternative<std::monostate>(parsedVpdMap))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001434 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301435 types::ObjectMap objectInterfaceMap;
1436 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1437
1438 // Notify PIM
1439 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1440 {
1441 throw std::runtime_error(
1442 std::string(__FUNCTION__) +
1443 "Call to PIM failed while publishing VPD.");
1444 }
1445 }
1446 else
1447 {
1448 logging::logMessage("Empty parsedVpdMap recieved for path [" +
1449 i_vpdFilePath + "]. Check PEL for reason.");
Anupama B R4c65fcd2025-09-01 08:09:00 -05001450
1451 // As empty parsedVpdMap recieved for some reason, but still
1452 // considered VPD collection is completed. Hence FRU collection
1453 // Status will be set as completed.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001454 }
1455 }
1456 catch (const std::exception& ex)
1457 {
Anupama B R24691d22025-05-21 08:14:15 -05001458 setCollectionStatusProperty(i_vpdFilePath,
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001459 constants::vpdCollectionFailed);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001460
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001461 // handle all the exceptions internally. Return only true/false
1462 // based on status of execution.
1463 if (typeid(ex) == std::type_index(typeid(DataException)))
1464 {
Rekha Aparna017567a2025-08-13 02:07:06 -05001465 uint16_t l_errCode = 0;
Sunny Srivastava78c91072025-02-05 14:09:50 +05301466 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1467 // logging error for these cases.
1468 if (vpdSpecificUtility::isPass1Planar())
1469 {
Rekha Aparna017567a2025-08-13 02:07:06 -05001470 std::string l_invPath =
1471 jsonUtility::getInventoryObjPathFromJson(
1472 m_parsedJson, i_vpdFilePath, l_errCode);
1473
1474 if (l_errCode != 0)
1475 {
1476 logging::logMessage(
1477 "Failed to get inventory object path from JSON for FRU [" +
1478 i_vpdFilePath + "], error: " +
1479 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1480 }
1481
RekhaAparna011ef21002025-02-18 23:47:36 -06001482 const std::string& l_invPathLeafValue =
Rekha Aparna017567a2025-08-13 02:07:06 -05001483 sdbusplus::message::object_path(l_invPath).filename();
Sunny Srivastava78c91072025-02-05 14:09:50 +05301484
RekhaAparna011ef21002025-02-18 23:47:36 -06001485 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1486 std::string::npos))
Sunny Srivastava78c91072025-02-05 14:09:50 +05301487 {
1488 // skip logging any PEL for PCIe cards on pass 1 planar.
1489 return std::make_tuple(false, i_vpdFilePath);
1490 }
1491 }
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301492 }
Sunny Srivastava78c91072025-02-05 14:09:50 +05301493
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301494 EventLogger::createSyncPel(
Souvik Roy37c6bef2025-07-17 00:55:59 -05001495 EventLogger::getErrorType(ex),
1496 (typeid(ex) == typeid(DataException)) ||
1497 (typeid(ex) == typeid(EccException))
1498 ? types::SeverityType::Warning
1499 : types::SeverityType::Informational,
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301500 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1501 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001502
1503 // TODO: Figure out a way to clear data in case of any failure at
1504 // runtime.
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301505
1506 // set present property to false for any error case. In future this will
1507 // be replaced by presence logic.
Souvik Roy6a9553c2025-02-07 01:16:32 -06001508 // Update Present property for this FRU only if we handle Present
1509 // property for the FRU.
1510 if (isPresentPropertyHandlingRequired(
1511 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1512 {
1513 setPresentProperty(i_vpdFilePath, false);
1514 }
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301515
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001516 m_semaphore.release();
1517 return std::make_tuple(false, i_vpdFilePath);
1518 }
Anupama B R4c65fcd2025-09-01 08:09:00 -05001519
1520 setCollectionStatusProperty(i_vpdFilePath,
1521 constants::vpdCollectionCompleted);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001522 m_semaphore.release();
1523 return std::make_tuple(true, i_vpdFilePath);
1524}
1525
Sunny Srivastava61611752025-02-04 00:29:33 -06001526bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1527{
1528 if (i_vpdFilePath.empty())
1529 {
1530 return true;
1531 }
1532
1533 // skip processing of system VPD again as it has been already collected.
1534 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1535 {
1536 return true;
1537 }
1538
1539 if (dbusUtility::isChassisPowerOn())
1540 {
1541 // If chassis is powered on, skip collecting FRUs which are
1542 // powerOffOnly.
1543 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath))
1544 {
1545 return true;
1546 }
1547
Rekha Aparna017567a2025-08-13 02:07:06 -05001548 uint16_t l_errCode = 0;
1549 std::string l_invPath = jsonUtility::getInventoryObjPathFromJson(
1550 m_parsedJson, i_vpdFilePath, l_errCode);
1551
1552 if (l_errCode)
1553 {
1554 logging::logMessage(
1555 "Failed to get inventory path from JSON for FRU [" +
1556 i_vpdFilePath +
1557 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
1558
1559 return false;
1560 }
1561
Sunny Srivastava61611752025-02-04 00:29:33 -06001562 const std::string& l_invPathLeafValue =
Rekha Aparna017567a2025-08-13 02:07:06 -05001563 sdbusplus::message::object_path(l_invPath).filename();
Sunny Srivastava61611752025-02-04 00:29:33 -06001564
1565 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1566 {
1567 return true;
1568 }
1569 }
1570
1571 return false;
1572}
1573
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001574void Worker::collectFrusFromJson()
1575{
1576 // A parsed JSON file should be present to pick FRUs EEPROM paths
1577 if (m_parsedJson.empty())
1578 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301579 throw JsonException(
1580 std::string(__FUNCTION__) +
1581 ": Config JSON is mandatory for processing of FRUs through this API.",
1582 m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001583 }
1584
1585 const nlohmann::json& listOfFrus =
1586 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1587
1588 for (const auto& itemFRUS : listOfFrus.items())
1589 {
1590 const std::string& vpdFilePath = itemFRUS.key();
1591
Sunny Srivastava61611752025-02-04 00:29:33 -06001592 if (skipPathForCollection(vpdFilePath))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001593 {
1594 continue;
1595 }
1596
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001597 try
1598 {
1599 std::thread{[vpdFilePath, this]() {
1600 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001601
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001602 m_mutex.lock();
1603 m_activeCollectionThreadCount--;
1604 m_mutex.unlock();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001605
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001606 if (!m_activeCollectionThreadCount)
1607 {
1608 m_isAllFruCollected = true;
1609 }
1610 }}.detach();
1611 }
1612 catch (const std::exception& l_ex)
1613 {
1614 // add vpdFilePath(EEPROM path) to failed list
1615 m_failedEepromPaths.push_front(vpdFilePath);
1616 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001617 }
1618}
1619
1620// ToDo: Move the API under IBM_SYSTEM
1621void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1622{
1623 try
1624 {
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001625 uint16_t l_errCode = 0;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001626 std::string l_backupAndRestoreCfgFilePath =
1627 m_parsedJson.value("backupRestoreConfigPath", "");
1628
1629 nlohmann::json l_backupAndRestoreCfgJsonObj =
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001630 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath,
1631 l_errCode);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001632
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001633 if (l_errCode)
RekhaAparna011ef21002025-02-18 23:47:36 -06001634 {
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001635 throw JsonException(
1636 "JSON parsing failed for file [ " +
1637 l_backupAndRestoreCfgFilePath + " ], error : " +
1638 vpdSpecificUtility::getErrCodeMsg(l_errCode),
1639 l_backupAndRestoreCfgFilePath);
RekhaAparna011ef21002025-02-18 23:47:36 -06001640 }
1641
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001642 // check if either of "source" or "destination" has inventory path.
1643 // this indicates that this sytem has System VPD on hardware
1644 // and other copy on D-Bus (BMC cache).
1645 if (!l_backupAndRestoreCfgJsonObj.empty() &&
1646 ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1647 l_backupAndRestoreCfgJsonObj["source"].contains(
1648 "inventoryPath")) ||
1649 (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1650 l_backupAndRestoreCfgJsonObj["destination"].contains(
1651 "inventoryPath"))))
1652 {
1653 BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1654 auto [l_srcVpdVariant,
1655 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1656
1657 // ToDo: Revisit is this check is required or not.
1658 if (auto l_srcVpdMap =
1659 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1660 l_srcVpdMap && !(*l_srcVpdMap).empty())
1661 {
1662 io_srcVpdMap = std::move(l_srcVpdVariant);
1663 }
1664 }
1665 }
1666 catch (const std::exception& l_ex)
1667 {
1668 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301669 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301670 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001671 std::string(
1672 "Exception caught while backup and restore VPD keyword's.") +
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301673 EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001674 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1675 }
1676}
1677
1678void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1679{
1680 if (i_dbusObjPath.empty())
1681 {
1682 throw std::runtime_error("Given DBus object path is empty.");
1683 }
1684
Rekha Aparna0578dd22025-09-02 08:20:21 -05001685 uint16_t l_errCode = 0;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001686 const std::string& l_fruPath =
Rekha Aparna0578dd22025-09-02 08:20:21 -05001687 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath, l_errCode);
1688
1689 if (l_errCode)
1690 {
1691 logging::logMessage(
1692 "Failed to get FRU path for inventory path [" + i_dbusObjPath +
1693 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode) +
1694 " Aborting FRU VPD deletion.");
1695 return;
1696 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001697
1698 try
1699 {
1700 auto l_presentPropValue = dbusUtility::readDbusProperty(
1701 constants::pimServiceName, i_dbusObjPath,
1702 constants::inventoryItemInf, "Present");
1703
1704 if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1705 {
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001706 uint16_t l_errCode = 0;
Souvik Roye9120152025-07-02 08:24:38 -05001707 // check if FRU's Present property is handled by vpd-manager
1708 const auto& l_isFruPresenceHandled =
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001709 jsonUtility::isFruPresenceHandled(m_parsedJson, l_fruPath,
1710 l_errCode);
1711
1712 if (l_errCode)
1713 {
1714 throw std::runtime_error(
1715 "Failed to check if FRU's presence is handled, reason: " +
1716 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1717 }
Souvik Roye9120152025-07-02 08:24:38 -05001718
1719 if (!(*l_value) && l_isFruPresenceHandled)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001720 {
1721 throw std::runtime_error("Given FRU is not present");
1722 }
Souvik Roye9120152025-07-02 08:24:38 -05001723 else if (*l_value && !l_isFruPresenceHandled)
1724 {
1725 throw std::runtime_error(
1726 "Given FRU is present and its presence is not handled by vpd-manager.");
1727 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001728 else
1729 {
1730 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001731 "preAction", "deletion",
1732 l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001733 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001734 if (!processPreAction(l_fruPath, "deletion", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001735 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001736 std::string l_msg = "Pre action failed";
1737 if (l_errCode)
1738 {
1739 l_msg +=
1740 " Reason: " +
1741 vpdSpecificUtility::getErrCodeMsg(l_errCode);
1742 }
1743 throw std::runtime_error(l_msg);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001744 }
1745 }
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001746 else if (l_errCode)
1747 {
1748 logging::logMessage(
1749 "Failed to check if pre action required for FRU [" +
1750 l_fruPath + "], error : " +
1751 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1752 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001753
1754 std::vector<std::string> l_interfaceList{
1755 constants::operationalStatusInf};
1756
1757 types::MapperGetSubTree l_subTreeMap =
1758 dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1759 l_interfaceList);
1760
1761 types::ObjectMap l_objectMap;
1762
1763 // Updates VPD specific interfaces property value under PIM for
1764 // sub FRUs.
1765 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1766 l_subTreeMap)
1767 {
1768 types::InterfaceMap l_interfaceMap;
1769 vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1770 l_interfaceMap);
1771 l_objectMap.emplace(l_objectPath,
1772 std::move(l_interfaceMap));
1773 }
1774
1775 types::InterfaceMap l_interfaceMap;
1776 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1777 l_interfaceMap);
1778
1779 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1780
1781 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1782 {
1783 throw std::runtime_error("Call to PIM failed.");
1784 }
1785
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001786 l_errCode = 0;
1787
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001788 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001789 "postAction", "deletion",
1790 l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001791 {
1792 if (!processPostAction(l_fruPath, "deletion"))
1793 {
1794 throw std::runtime_error("Post action failed");
1795 }
1796 }
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001797 else if (l_errCode)
1798 {
1799 logging::logMessage(
1800 "Failed to check if post action required during deletion for FRU [" +
1801 l_fruPath + "], error : " +
1802 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1803 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001804 }
1805 }
1806 else
1807 {
1808 logging::logMessage(
1809 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1810 "] as unable to read present property");
1811 return;
1812 }
1813
1814 logging::logMessage(
1815 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1816 }
1817 catch (const std::exception& l_ex)
1818 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001819 uint16_t l_errCode = 0;
1820 std::string l_errMsg =
1821 "Failed to delete VPD for FRU : " + i_dbusObjPath +
1822 " error: " + std::string(l_ex.what());
1823
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001824 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001825 "postFailAction", "deletion",
1826 l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001827 {
1828 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001829 "deletion", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001830 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001831 l_errMsg += ". Post fail action also failed, error : " +
1832 vpdSpecificUtility::getErrCodeMsg(l_errCode);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001833 }
1834 }
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001835 else if (l_errCode)
1836 {
1837 l_errMsg +=
1838 ". Failed to check if post fail action required, error : " +
1839 vpdSpecificUtility::getErrCodeMsg(l_errCode);
1840 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001841
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001842 logging::logMessage(l_errMsg);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001843 }
1844}
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301845
1846void Worker::setPresentProperty(const std::string& i_vpdPath,
1847 const bool& i_value)
1848{
1849 try
1850 {
1851 if (i_vpdPath.empty())
1852 {
1853 throw std::runtime_error(
1854 "Path is empty. Can't set present property");
1855 }
1856
1857 types::ObjectMap l_objectInterfaceMap;
1858
1859 // If the given path is EEPROM path.
1860 if (m_parsedJson["frus"].contains(i_vpdPath))
1861 {
1862 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1863 {
1864 sdbusplus::message::object_path l_fruObjectPath(
1865 l_Fru["inventoryPath"]);
1866
1867 types::PropertyMap l_propertyValueMap;
1868 l_propertyValueMap.emplace("Present", i_value);
1869
1870 types::InterfaceMap l_interfaces;
1871 vpdSpecificUtility::insertOrMerge(l_interfaces,
1872 constants::inventoryItemInf,
1873 move(l_propertyValueMap));
1874
1875 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1876 std::move(l_interfaces));
1877 }
1878 }
1879 else
1880 {
1881 // consider it as an inventory path.
1882 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1883 {
1884 throw std::runtime_error(
1885 "Invalid inventory path: " + i_vpdPath);
1886 }
1887
1888 types::PropertyMap l_propertyValueMap;
1889 l_propertyValueMap.emplace("Present", i_value);
1890
1891 types::InterfaceMap l_interfaces;
1892 vpdSpecificUtility::insertOrMerge(l_interfaces,
1893 constants::inventoryItemInf,
1894 move(l_propertyValueMap));
1895
1896 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1897 }
1898
1899 // Notify PIM
1900 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1901 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301902 throw DbusException(
1903 std::string(__FUNCTION__) +
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301904 "Call to PIM failed while setting present property for path " +
1905 i_vpdPath);
1906 }
1907 }
1908 catch (const std::exception& l_ex)
1909 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301910 EventLogger::createSyncPel(
1911 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1912 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1913 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301914 }
1915}
1916
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301917void Worker::performVpdRecollection()
1918{
1919 try
1920 {
1921 // Check if system config JSON is present
1922 if (m_parsedJson.empty())
1923 {
1924 throw std::runtime_error(
1925 "System config json object is empty, can't process recollection.");
1926 }
1927
1928 const auto& l_frusReplaceableAtStandby =
1929 jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson);
1930
1931 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1932 {
1933 // ToDo: Add some logic/trace to know the flow to
1934 // collectSingleFruVpd has been directed via
1935 // performVpdRecollection.
1936 collectSingleFruVpd(l_fruInventoryPath);
1937 }
1938 return;
1939 }
1940
1941 catch (const std::exception& l_ex)
1942 {
1943 // TODO Log PEL
1944 logging::logMessage(
1945 "VPD recollection failed with error: " + std::string(l_ex.what()));
1946 }
1947}
1948
1949void Worker::collectSingleFruVpd(
1950 const sdbusplus::message::object_path& i_dbusObjPath)
1951{
Anupama B R48f297b2025-08-13 04:29:06 -05001952 std::string l_fruPath{};
Rekha Aparna0578dd22025-09-02 08:20:21 -05001953 uint16_t l_errCode = 0;
1954
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301955 try
1956 {
1957 // Check if system config JSON is present
1958 if (m_parsedJson.empty())
1959 {
1960 logging::logMessage(
1961 "System config JSON object not present. Single FRU VPD collection is not performed for " +
1962 std::string(i_dbusObjPath));
1963 return;
1964 }
1965
1966 // Get FRU path for the given D-bus object path from JSON
Rekha Aparna0578dd22025-09-02 08:20:21 -05001967 l_fruPath = jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath,
1968 l_errCode);
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301969
1970 if (l_fruPath.empty())
1971 {
Rekha Aparna0578dd22025-09-02 08:20:21 -05001972 if (l_errCode)
1973 {
1974 logging::logMessage(
1975 "Failed to get FRU path for [" +
1976 std::string(i_dbusObjPath) + "], error : " +
1977 vpdSpecificUtility::getErrCodeMsg(l_errCode) +
1978 " Aborting single FRU VPD collection.");
1979 return;
1980 }
1981
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301982 logging::logMessage(
1983 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
1984 std::string(i_dbusObjPath));
1985 return;
1986 }
1987
1988 // Check if host is up and running
1989 if (dbusUtility::isHostRunning())
1990 {
Rekha Aparnaad0db9e2025-09-01 20:29:18 -05001991 uint16_t l_errCode = 0;
1992 bool isFruReplaceableAtRuntime =
1993 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
1994 l_errCode);
1995
1996 if (l_errCode)
1997 {
1998 logging::logMessage(
1999 "Failed to check if FRU is replaceable at runtime for FRU : [" +
2000 std::string(i_dbusObjPath) + "], error : " +
2001 vpdSpecificUtility::getErrCodeMsg(l_errCode));
2002 return;
2003 }
2004
2005 if (!isFruReplaceableAtRuntime)
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302006 {
2007 logging::logMessage(
2008 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
2009 std::string(i_dbusObjPath));
2010 return;
2011 }
2012 }
2013 else if (dbusUtility::isBMCReady())
2014 {
Rekha Aparna40845612025-09-01 19:58:56 -05002015 uint16_t l_errCode = 0;
2016 bool isFruReplaceableAtStandby =
2017 jsonUtility::isFruReplaceableAtStandby(m_parsedJson, l_fruPath,
2018 l_errCode);
2019
2020 if (l_errCode)
2021 {
2022 logging::logMessage(
2023 "Error while checking if FRU is replaceable at standby for FRU [" +
2024 std::string(i_dbusObjPath) + "], error : " +
2025 vpdSpecificUtility::getErrCodeMsg(l_errCode));
2026 }
2027
Rekha Aparnaad0db9e2025-09-01 20:29:18 -05002028 l_errCode = 0;
2029 bool isFruReplaceableAtRuntime =
2030 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
2031 l_errCode);
2032
2033 if (l_errCode)
2034 {
2035 logging::logMessage(
2036 "Failed to check if FRU is replaceable at runtime for FRU : [" +
2037 std::string(i_dbusObjPath) + "], error : " +
2038 vpdSpecificUtility::getErrCodeMsg(l_errCode));
2039 return;
2040 }
2041
2042 if (!isFruReplaceableAtStandby && (!isFruReplaceableAtRuntime))
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302043 {
2044 logging::logMessage(
2045 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
2046 std::string(i_dbusObjPath));
2047 return;
2048 }
2049 }
2050
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002051 // Set collection Status as InProgress. Since it's an intermediate state
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302052 // D-bus set-property call is good enough to update the status.
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002053 const std::string& l_collStatusProp = "Status";
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302054
Anupama B R4c65fcd2025-09-01 08:09:00 -05002055 setCollectionStatusProperty(l_fruPath,
2056 constants::vpdCollectionInProgress);
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302057
2058 // Parse VPD
2059 types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
2060
2061 // If l_parsedVpd is pointing to std::monostate
2062 if (l_parsedVpd.index() == 0)
2063 {
2064 throw std::runtime_error(
2065 "VPD parsing failed for " + std::string(i_dbusObjPath));
2066 }
2067
2068 // Get D-bus object map from worker class
2069 types::ObjectMap l_dbusObjectMap;
2070 populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
2071
2072 if (l_dbusObjectMap.empty())
2073 {
2074 throw std::runtime_error(
2075 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
2076 std::string(i_dbusObjPath));
2077 }
2078
2079 // Call PIM's Notify method
2080 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
2081 {
2082 throw std::runtime_error(
2083 "Notify PIM failed. Single FRU VPD collection failed for " +
2084 std::string(i_dbusObjPath));
2085 }
Anupama B R4c65fcd2025-09-01 08:09:00 -05002086 setCollectionStatusProperty(l_fruPath,
2087 constants::vpdCollectionCompleted);
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302088 }
2089 catch (const std::exception& l_error)
2090 {
Anupama B R48f297b2025-08-13 04:29:06 -05002091 setCollectionStatusProperty(l_fruPath, constants::vpdCollectionFailed);
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302092 // TODO: Log PEL
2093 logging::logMessage(std::string(l_error.what()));
2094 }
2095}
Anupama B R24691d22025-05-21 08:14:15 -05002096
2097void Worker::setCollectionStatusProperty(
2098 const std::string& i_vpdPath, const std::string& i_value) const noexcept
2099{
2100 try
2101 {
2102 if (i_vpdPath.empty())
2103 {
2104 throw std::runtime_error(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002105 "Given path is empty. Can't set collection Status property");
Anupama B R24691d22025-05-21 08:14:15 -05002106 }
2107
Anupama B R4c65fcd2025-09-01 08:09:00 -05002108 types::PropertyMap l_timeStampMap;
2109 if (i_value == constants::vpdCollectionCompleted ||
2110 i_value == constants::vpdCollectionFailed)
2111 {
2112 l_timeStampMap.emplace(
2113 "CompletedTime",
2114 types::DbusVariantType{
2115 commonUtility::getCurrentTimeSinceEpoch()});
2116 }
2117 else if (i_value == constants::vpdCollectionInProgress)
2118 {
2119 l_timeStampMap.emplace(
2120 "StartTime", types::DbusVariantType{
2121 commonUtility::getCurrentTimeSinceEpoch()});
2122 }
2123 else if (i_value == constants::vpdCollectionNotStarted)
2124 {
2125 l_timeStampMap.emplace("StartTime", 0);
2126 l_timeStampMap.emplace("CompletedTime", 0);
2127 }
2128
Anupama B R24691d22025-05-21 08:14:15 -05002129 types::ObjectMap l_objectInterfaceMap;
2130
2131 if (m_parsedJson["frus"].contains(i_vpdPath))
2132 {
2133 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
2134 {
2135 sdbusplus::message::object_path l_fruObjectPath(
2136 l_Fru["inventoryPath"]);
2137
2138 types::PropertyMap l_propertyValueMap;
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002139 l_propertyValueMap.emplace("Status", i_value);
Anupama B R4c65fcd2025-09-01 08:09:00 -05002140 l_propertyValueMap.insert(l_timeStampMap.begin(),
2141 l_timeStampMap.end());
Anupama B R24691d22025-05-21 08:14:15 -05002142
2143 types::InterfaceMap l_interfaces;
2144 vpdSpecificUtility::insertOrMerge(
2145 l_interfaces, constants::vpdCollectionInterface,
2146 move(l_propertyValueMap));
2147
2148 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
2149 std::move(l_interfaces));
2150 }
2151 }
2152 else
2153 {
2154 // consider it as an inventory path.
2155 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
2156 {
2157 throw std::runtime_error(
2158 "Invalid inventory path: " + i_vpdPath +
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002159 ". Can't set collection Status property");
Anupama B R24691d22025-05-21 08:14:15 -05002160 }
2161
2162 types::PropertyMap l_propertyValueMap;
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002163 l_propertyValueMap.emplace("Status", i_value);
Anupama B R4c65fcd2025-09-01 08:09:00 -05002164 l_propertyValueMap.insert(l_timeStampMap.begin(),
2165 l_timeStampMap.end());
Anupama B R24691d22025-05-21 08:14:15 -05002166
2167 types::InterfaceMap l_interfaces;
2168 vpdSpecificUtility::insertOrMerge(l_interfaces,
2169 constants::vpdCollectionInterface,
2170 move(l_propertyValueMap));
2171
2172 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
2173 }
2174
2175 // Notify PIM
2176 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
2177 {
2178 throw DbusException(
2179 std::string(__FUNCTION__) +
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002180 "Call to PIM failed while setting collection Status property for path " +
Anupama B R24691d22025-05-21 08:14:15 -05002181 i_vpdPath);
2182 }
2183 }
2184 catch (const std::exception& l_ex)
2185 {
2186 EventLogger::createSyncPel(
2187 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
2188 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
2189 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
2190 }
2191}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05002192} // namespace vpd