blob: c2f9621b329981b45b21f4fc67464e3578d3aa0d [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
Rekha Aparna196e3082025-09-08 20:40:35 -0500427 if (isSystemVPDOnDBus())
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500428 {
Rekha Aparna196e3082025-09-08 20:40:35 -0500429 uint16_t l_errCode = 0;
430 if (jsonUtility::isBackupAndRestoreRequired(m_parsedJson,
431 l_errCode))
432 {
433 performBackupAndRestore(parsedVpdMap);
434 }
435 else if (l_errCode)
436 {
437 logging::logMessage(
438 "Failed to check if backup and restore required. Reason : " +
439 vpdSpecificUtility::getErrCodeMsg(l_errCode));
440 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500441 }
442
443 // proceed to publish system VPD.
444 publishSystemVPD(parsedVpdMap);
Anupama B R4c65fcd2025-09-01 08:09:00 -0500445 setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH,
446 constants::vpdCollectionCompleted);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500447 return;
448 }
449
450 setEnvAndReboot("fitconfig", devTreeFromJson);
451 exit(EXIT_SUCCESS);
452}
453
454void Worker::populateIPZVPDpropertyMap(
455 types::InterfaceMap& interfacePropMap,
456 const types::IPZKwdValueMap& keyordValueMap,
457 const std::string& interfaceName)
458{
459 types::PropertyMap propertyValueMap;
460 for (const auto& kwdVal : keyordValueMap)
461 {
462 auto kwd = kwdVal.first;
463
464 if (kwd[0] == '#')
465 {
466 kwd = std::string("PD_") + kwd[1];
467 }
468 else if (isdigit(kwd[0]))
469 {
470 kwd = std::string("N_") + kwd;
471 }
472
473 types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end());
474 propertyValueMap.emplace(move(kwd), move(value));
475 }
476
477 if (!propertyValueMap.empty())
478 {
479 interfacePropMap.emplace(interfaceName, propertyValueMap);
480 }
481}
482
483void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
484 types::InterfaceMap& interfaceMap)
485{
486 for (const auto& kwdValMap : keyordVPDMap)
487 {
488 types::PropertyMap propertyValueMap;
489 auto kwd = kwdValMap.first;
490
491 if (kwd[0] == '#')
492 {
493 kwd = std::string("PD_") + kwd[1];
494 }
495 else if (isdigit(kwd[0]))
496 {
497 kwd = std::string("N_") + kwd;
498 }
499
500 if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second))
501 {
502 types::BinaryVector value((*keywordValue).begin(),
503 (*keywordValue).end());
504 propertyValueMap.emplace(move(kwd), move(value));
505 }
506 else if (auto keywordValue = get_if<std::string>(&kwdValMap.second))
507 {
508 types::BinaryVector value((*keywordValue).begin(),
509 (*keywordValue).end());
510 propertyValueMap.emplace(move(kwd), move(value));
511 }
512 else if (auto keywordValue = get_if<size_t>(&kwdValMap.second))
513 {
514 if (kwd == "MemorySizeInKB")
515 {
516 types::PropertyMap memProp;
517 memProp.emplace(move(kwd), ((*keywordValue)));
518 interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm",
519 move(memProp));
520 continue;
521 }
522 else
523 {
524 logging::logMessage(
525 "Unknown Keyword =" + kwd + " found in keyword VPD map");
526 continue;
527 }
528 }
529 else
530 {
531 logging::logMessage(
532 "Unknown variant type found in keyword VPD map.");
533 continue;
534 }
535
536 if (!propertyValueMap.empty())
537 {
538 vpdSpecificUtility::insertOrMerge(
539 interfaceMap, constants::kwdVpdInf, move(propertyValueMap));
540 }
541 }
542}
543
544void Worker::populateInterfaces(const nlohmann::json& interfaceJson,
545 types::InterfaceMap& interfaceMap,
546 const types::VPDMapVariant& parsedVpdMap)
547{
548 for (const auto& interfacesPropPair : interfaceJson.items())
549 {
550 const std::string& interface = interfacesPropPair.key();
551 types::PropertyMap propertyMap;
552
553 for (const auto& propValuePair : interfacesPropPair.value().items())
554 {
555 const std::string property = propValuePair.key();
556
557 if (propValuePair.value().is_boolean())
558 {
559 propertyMap.emplace(property,
560 propValuePair.value().get<bool>());
561 }
562 else if (propValuePair.value().is_string())
563 {
564 if (property.compare("LocationCode") == 0 &&
565 interface.compare("com.ibm.ipzvpd.Location") == 0)
566 {
567 std::string value =
568 vpdSpecificUtility::getExpandedLocationCode(
569 propValuePair.value().get<std::string>(),
570 parsedVpdMap);
571 propertyMap.emplace(property, value);
572
573 auto l_locCodeProperty = propertyMap;
574 vpdSpecificUtility::insertOrMerge(
575 interfaceMap,
576 std::string(constants::xyzLocationCodeInf),
577 move(l_locCodeProperty));
578 }
579 else
580 {
581 propertyMap.emplace(
582 property, propValuePair.value().get<std::string>());
583 }
584 }
585 else if (propValuePair.value().is_array())
586 {
587 try
588 {
589 propertyMap.emplace(
590 property,
591 propValuePair.value().get<types::BinaryVector>());
592 }
593 catch (const nlohmann::detail::type_error& e)
594 {
595 std::cerr << "Type exception: " << e.what() << "\n";
596 }
597 }
598 else if (propValuePair.value().is_number())
599 {
600 // For now assume the value is a size_t. In the future it would
601 // be nice to come up with a way to get the type from the JSON.
602 propertyMap.emplace(property,
603 propValuePair.value().get<size_t>());
604 }
605 else if (propValuePair.value().is_object())
606 {
607 const std::string& record =
608 propValuePair.value().value("recordName", "");
609 const std::string& keyword =
610 propValuePair.value().value("keywordName", "");
611 const std::string& encoding =
612 propValuePair.value().value("encoding", "");
613
614 if (auto ipzVpdMap =
615 std::get_if<types::IPZVpdMap>(&parsedVpdMap))
616 {
617 if (!record.empty() && !keyword.empty() &&
618 (*ipzVpdMap).count(record) &&
619 (*ipzVpdMap).at(record).count(keyword))
620 {
621 auto encoded = vpdSpecificUtility::encodeKeyword(
622 ((*ipzVpdMap).at(record).at(keyword)), encoding);
623 propertyMap.emplace(property, encoded);
624 }
625 }
626 else if (auto kwdVpdMap =
627 std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
628 {
629 if (!keyword.empty() && (*kwdVpdMap).count(keyword))
630 {
631 if (auto kwValue = std::get_if<types::BinaryVector>(
632 &(*kwdVpdMap).at(keyword)))
633 {
634 auto encodedValue =
635 vpdSpecificUtility::encodeKeyword(
636 std::string((*kwValue).begin(),
637 (*kwValue).end()),
638 encoding);
639
640 propertyMap.emplace(property, encodedValue);
641 }
642 else if (auto kwValue = std::get_if<std::string>(
643 &(*kwdVpdMap).at(keyword)))
644 {
645 auto encodedValue =
646 vpdSpecificUtility::encodeKeyword(
647 std::string((*kwValue).begin(),
648 (*kwValue).end()),
649 encoding);
650
651 propertyMap.emplace(property, encodedValue);
652 }
653 else if (auto uintValue = std::get_if<size_t>(
654 &(*kwdVpdMap).at(keyword)))
655 {
656 propertyMap.emplace(property, *uintValue);
657 }
658 else
659 {
660 logging::logMessage(
661 "Unknown keyword found, Keywrod = " + keyword);
662 }
663 }
664 }
665 }
666 }
667 vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
668 move(propertyMap));
669 }
670}
671
672bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
673{
674 const unsigned char l_io[] = {
675 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
676 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
677
678 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
679 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
680 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
681 // IO.
682 if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
683 constants::SIZE_OF_8EQ_IN_PG) == 0)
684 {
685 return true;
686 }
687
688 // The CPU is not an IO
689 return false;
690}
691
692bool Worker::primeInventory(const std::string& i_vpdFilePath)
693{
694 if (i_vpdFilePath.empty())
695 {
696 logging::logMessage("Empty VPD file path given");
697 return false;
698 }
699
700 if (m_parsedJson.empty())
701 {
702 logging::logMessage("Empty JSON detected for " + i_vpdFilePath);
703 return false;
704 }
705 else if (!m_parsedJson["frus"].contains(i_vpdFilePath))
706 {
707 logging::logMessage("File " + i_vpdFilePath +
708 ", is not found in the system config JSON file.");
709 return false;
710 }
711
712 types::ObjectMap l_objectInterfaceMap;
713 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdFilePath])
714 {
715 types::InterfaceMap l_interfaces;
716 sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
717
718 if (l_Fru.contains("ccin"))
719 {
720 continue;
721 }
722
723 if (l_Fru.contains("noprime") && l_Fru.value("noprime", false))
724 {
725 continue;
726 }
727
Souvik Roy6a9553c2025-02-07 01:16:32 -0600728 // Reset data under PIM for this FRU only if the FRU is not synthesized
729 // and we handle it's Present property.
730 if (isPresentPropertyHandlingRequired(l_Fru))
731 {
732 // Clear data under PIM if already exists.
733 vpdSpecificUtility::resetDataUnderPIM(
734 std::string(l_Fru["inventoryPath"]), l_interfaces);
735 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500736
737 // Add extra interfaces mentioned in the Json config file
738 if (l_Fru.contains("extraInterfaces"))
739 {
740 populateInterfaces(l_Fru["extraInterfaces"], l_interfaces,
741 std::monostate{});
742 }
743
744 types::PropertyMap l_propertyValueMap;
Sunny Srivastavad159bb42025-01-09 11:13:50 +0530745
Souvik Roy6a9553c2025-02-07 01:16:32 -0600746 // Update Present property for this FRU only if we handle Present
747 // property for the FRU.
748 if (isPresentPropertyHandlingRequired(l_Fru))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500749 {
Souvik Roy6a9553c2025-02-07 01:16:32 -0600750 l_propertyValueMap.emplace("Present", false);
751
752 // TODO: Present based on file will be taken care in future.
753 // By default present is set to false for FRU at the time of
754 // priming. Once collection goes through, it will be set to true in
755 // that flow.
756 /*if (std::filesystem::exists(i_vpdFilePath))
757 {
758 l_propertyValueMap["Present"] = true;
759 }*/
760 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500761
762 vpdSpecificUtility::insertOrMerge(l_interfaces,
763 "xyz.openbmc_project.Inventory.Item",
764 move(l_propertyValueMap));
765
766 if (l_Fru.value("inherit", true) &&
767 m_parsedJson.contains("commonInterfaces"))
768 {
769 populateInterfaces(m_parsedJson["commonInterfaces"], l_interfaces,
770 std::monostate{});
771 }
772
773 processFunctionalProperty(l_Fru["inventoryPath"], l_interfaces);
774 processEnabledProperty(l_Fru["inventoryPath"], l_interfaces);
775
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -0600776 // Emplace the default state of FRU VPD collection
777 types::PropertyMap l_fruCollectionProperty = {
Anupama B R5cd1b2d2025-08-05 04:57:40 -0500778 {"Status", constants::vpdCollectionNotStarted}};
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -0600779
780 vpdSpecificUtility::insertOrMerge(l_interfaces,
781 constants::vpdCollectionInterface,
782 std::move(l_fruCollectionProperty));
783
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500784 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
785 std::move(l_interfaces));
786 }
787
788 // Notify PIM
789 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
790 {
791 logging::logMessage("Call to PIM failed for VPD file " + i_vpdFilePath);
792 return false;
793 }
794
795 return true;
796}
797
798void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
799 types::InterfaceMap& interfaces)
800{
801 // embedded property(true or false) says whether the subfru is embedded
802 // into the parent fru (or) not. VPD sets Present property only for
803 // embedded frus. If the subfru is not an embedded FRU, the subfru may
804 // or may not be physically present. Those non embedded frus will always
805 // have Present=false irrespective of its physical presence or absence.
806 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
807 // Present to true for such sub frus.
808 // Eg: ethernet port is embedded into bmc card. So set Present to true
809 // for such sub frus. Also donot populate present property for embedded
810 // subfru which is synthesized. Currently there is no subfru which are
811 // both embedded and synthesized. But still the case is handled here.
812
813 // Check if its required to handle presence for this FRU.
814 if (singleFru.value("handlePresence", true))
815 {
816 types::PropertyMap presProp;
817 presProp.emplace("Present", true);
818 vpdSpecificUtility::insertOrMerge(
819 interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp));
820 }
821}
822
823void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
824 types::InterfaceMap& interfaces,
825 const types::VPDMapVariant& parsedVpdMap)
826{
827 populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
828 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
829 {
830 if (singleFru["extraInterfaces"].contains(
831 "xyz.openbmc_project.Inventory.Item.Cpu"))
832 {
833 auto itrToRec = (*ipzVpdMap).find("CP00");
834 if (itrToRec == (*ipzVpdMap).end())
835 {
836 return;
837 }
838
Souvik Roya55fcca2025-02-19 01:33:58 -0600839 const std::string pgKeywordValue{
840 vpdSpecificUtility::getKwVal(itrToRec->second, "PG")};
841
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500842 if (!pgKeywordValue.empty())
843 {
844 if (isCPUIOGoodOnly(pgKeywordValue))
845 {
846 interfaces["xyz.openbmc_project.Inventory.Item"]
847 ["PrettyName"] = "IO Module";
848 }
849 }
Souvik Roya55fcca2025-02-19 01:33:58 -0600850 else
851 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +0530852 throw DataException(std::string(__FUNCTION__) +
853 "Failed to get value for keyword PG");
Souvik Roya55fcca2025-02-19 01:33:58 -0600854 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500855 }
856 }
857}
858
859void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
860 const types::VPDMapVariant& parsedVpdMap,
861 types::InterfaceMap& interfaces)
862{
863 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
864 {
865 for (const auto& record : singleFru["copyRecords"])
866 {
867 const std::string& recordName = record;
868 if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
869 {
870 populateIPZVPDpropertyMap(interfaces,
871 (*ipzVpdMap).at(recordName),
872 constants::ipzVpdInf + recordName);
873 }
874 }
875 }
876}
877
878void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
879 types::InterfaceMap& interfaces)
880{
881 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
882 {
883 for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
884 {
885 populateIPZVPDpropertyMap(interfaces, kwdValueMap,
886 constants::ipzVpdInf + recordName);
887 }
888 }
889 else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
890 {
891 populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
892 }
893
894 if (m_parsedJson.contains("commonInterfaces"))
895 {
896 populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
897 parsedVpdMap);
898 }
899}
900
901bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
902 const types::VPDMapVariant& parsedVpdMap)
903{
904 if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
905 {
906 auto itrToRec = (*ipzVPDMap).find("VINI");
907 if (itrToRec == (*ipzVPDMap).end())
908 {
909 return false;
910 }
911
Souvik Roya55fcca2025-02-19 01:33:58 -0600912 std::string ccinFromVpd{
913 vpdSpecificUtility::getKwVal(itrToRec->second, "CC")};
914
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500915 if (ccinFromVpd.empty())
916 {
917 return false;
918 }
919
920 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
921 ::toupper);
922
923 std::vector<std::string> ccinList;
924 for (std::string ccin : singleFru["ccin"])
925 {
926 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
927 ccinList.push_back(ccin);
928 }
929
930 if (ccinList.empty())
931 {
932 return false;
933 }
934
935 if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
936 ccinList.end())
937 {
938 return false;
939 }
940 }
941 return true;
942}
943
944void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
945 types::InterfaceMap& io_interfaces)
946{
947 if (!dbusUtility::isChassisPowerOn())
948 {
949 std::array<const char*, 1> l_operationalStatusInf = {
950 constants::operationalStatusInf};
951
952 auto mapperObjectMap = dbusUtility::getObjectMap(
953 i_inventoryObjPath, l_operationalStatusInf);
954
955 // If the object has been found. Check if it is under PIM.
956 if (mapperObjectMap.size() != 0)
957 {
958 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
959 {
960 if (l_serviceName == constants::pimServiceName)
961 {
962 // The object is already under PIM. No need to process
963 // again. Retain the old value.
964 return;
965 }
966 }
967 }
968
969 // Implies value is not there in D-Bus. Populate it with default
970 // value "true".
971 types::PropertyMap l_functionalProp;
972 l_functionalProp.emplace("Functional", true);
973 vpdSpecificUtility::insertOrMerge(io_interfaces,
974 constants::operationalStatusInf,
975 move(l_functionalProp));
976 }
977
978 // if chassis is power on. Functional property should be there on D-Bus.
979 // Don't process.
980 return;
981}
982
983void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
984 types::InterfaceMap& io_interfaces)
985{
986 if (!dbusUtility::isChassisPowerOn())
987 {
988 std::array<const char*, 1> l_enableInf = {constants::enableInf};
989
990 auto mapperObjectMap =
991 dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
992
993 // If the object has been found. Check if it is under PIM.
994 if (mapperObjectMap.size() != 0)
995 {
996 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
997 {
998 if (l_serviceName == constants::pimServiceName)
999 {
1000 // The object is already under PIM. No need to process
1001 // again. Retain the old value.
1002 return;
1003 }
1004 }
1005 }
1006
1007 // Implies value is not there in D-Bus. Populate it with default
1008 // value "true".
1009 types::PropertyMap l_enabledProp;
1010 l_enabledProp.emplace("Enabled", true);
1011 vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
1012 move(l_enabledProp));
1013 }
1014
1015 // if chassis is power on. Enabled property should be there on D-Bus.
1016 // Don't process.
1017 return;
1018}
1019
1020void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
1021 types::ObjectMap& objectInterfaceMap,
1022 const std::string& vpdFilePath)
1023{
1024 if (vpdFilePath.empty())
1025 {
1026 throw std::runtime_error(
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301027 std::string(__FUNCTION__) +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001028 "Invalid parameter passed to populateDbus API.");
1029 }
1030
1031 // JSON config is mandatory for processing of "if". Add "else" for any
1032 // processing without config JSON.
1033 if (!m_parsedJson.empty())
1034 {
1035 types::InterfaceMap interfaces;
1036
1037 for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
1038 {
1039 const auto& inventoryPath = aFru["inventoryPath"];
1040 sdbusplus::message::object_path fruObjectPath(inventoryPath);
1041 if (aFru.contains("ccin"))
1042 {
1043 if (!processFruWithCCIN(aFru, parsedVpdMap))
1044 {
1045 continue;
1046 }
1047 }
1048
1049 if (aFru.value("inherit", true))
1050 {
1051 processInheritFlag(parsedVpdMap, interfaces);
1052 }
1053
1054 // If specific record needs to be copied.
1055 if (aFru.contains("copyRecords"))
1056 {
1057 processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
1058 }
1059
1060 if (aFru.contains("extraInterfaces"))
1061 {
1062 // Process extra interfaces w.r.t a FRU.
1063 processExtraInterfaces(aFru, interfaces, parsedVpdMap);
1064 }
1065
1066 // Process FRUS which are embedded in the parent FRU and whose VPD
1067 // will be synthesized.
1068 if ((aFru.value("embedded", true)) &&
1069 (!aFru.value("synthesized", false)))
1070 {
1071 processEmbeddedAndSynthesizedFrus(aFru, interfaces);
1072 }
1073
1074 processFunctionalProperty(inventoryPath, interfaces);
1075 processEnabledProperty(inventoryPath, interfaces);
1076
1077 objectInterfaceMap.emplace(std::move(fruObjectPath),
1078 std::move(interfaces));
1079 }
1080 }
1081}
1082
Patrick Williams43fedab2025-02-03 14:28:05 -05001083std::string Worker::createAssetTagString(
1084 const types::VPDMapVariant& i_parsedVpdMap)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001085{
1086 std::string l_assetTag;
1087
1088 // system VPD will be in IPZ format.
1089 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
1090 {
1091 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
1092 if (l_itrToVsys != (*l_parsedVpdMap).end())
1093 {
Souvik Roya55fcca2025-02-19 01:33:58 -06001094 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal(
1095 l_itrToVsys->second, constants::kwdTM)};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001096
Souvik Roya55fcca2025-02-19 01:33:58 -06001097 if (l_tmKwdValue.empty())
1098 {
1099 throw std::runtime_error(
1100 std::string("Failed to get value for keyword [") +
1101 constants::kwdTM +
1102 std::string("] while creating Asset tag."));
1103 }
1104
1105 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal(
1106 l_itrToVsys->second, constants::kwdSE)};
1107
1108 if (l_seKwdValue.empty())
1109 {
1110 throw std::runtime_error(
1111 std::string("Failed to get value for keyword [") +
1112 constants::kwdSE +
1113 std::string("] while creating Asset tag."));
1114 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001115
1116 l_assetTag = std::string{"Server-"} + l_tmKwdValue +
1117 std::string{"-"} + l_seKwdValue;
1118 }
1119 else
1120 {
1121 throw std::runtime_error(
1122 "VSYS record not found in parsed VPD map to create Asset tag.");
1123 }
1124 }
1125 else
1126 {
1127 throw std::runtime_error(
1128 "Invalid VPD type recieved to create Asset tag.");
1129 }
1130
1131 return l_assetTag;
1132}
1133
1134void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
1135{
1136 types::ObjectMap objectInterfaceMap;
1137
1138 if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
1139 {
1140 populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
1141
1142 try
1143 {
1144 if (m_isFactoryResetDone)
1145 {
1146 const auto& l_assetTag = createAssetTagString(parsedVpdMap);
1147
1148 auto l_itrToSystemPath = objectInterfaceMap.find(
1149 sdbusplus::message::object_path(constants::systemInvPath));
1150 if (l_itrToSystemPath == objectInterfaceMap.end())
1151 {
1152 throw std::runtime_error(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301153 "Asset tag update failed. System Path not found in object map.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001154 }
1155
1156 types::PropertyMap l_assetTagProperty;
1157 l_assetTagProperty.emplace("AssetTag", l_assetTag);
1158
1159 (l_itrToSystemPath->second)
1160 .emplace(constants::assetTagInf,
1161 std::move(l_assetTagProperty));
1162 }
1163 }
1164 catch (const std::exception& l_ex)
1165 {
1166 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301167 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1168 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001169 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1170 }
1171
1172 // Notify PIM
1173 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1174 {
1175 throw std::runtime_error("Call to PIM failed for system VPD");
1176 }
1177 }
1178 else
1179 {
1180 throw DataException("Invalid format of parsed VPD map.");
1181 }
1182}
1183
1184bool Worker::processPreAction(const std::string& i_vpdFilePath,
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001185 const std::string& i_flagToProcess,
1186 uint16_t& i_errCode)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001187{
1188 if (i_vpdFilePath.empty() || i_flagToProcess.empty())
1189 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001190 i_errCode = error_code::INVALID_INPUT_PARAMETER;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001191 return false;
1192 }
1193
1194 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001195 i_vpdFilePath, i_flagToProcess,
1196 i_errCode)) &&
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001197 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
1198 {
1199 // TODO: Need a way to delete inventory object from Dbus and persisted
1200 // data section in case any FRU is not present or there is any
1201 // problem in collecting it. Once it has been deleted, it can be
1202 // re-created in the flow of priming the inventory. This needs to be
1203 // done either here or in the exception section of "parseAndPublishVPD"
1204 // API. Any failure in the process of collecting FRU will land up in the
1205 // excpetion of "parseAndPublishVPD".
1206
1207 // If the FRU is not there, clear the VINI/CCIN data.
1208 // Enity manager probes for this keyword to look for this
1209 // FRU, now if the data is persistent on BMC and FRU is
1210 // removed this can lead to ambiguity. Hence clearing this
1211 // Keyword if FRU is absent.
1212 const auto& inventoryPath =
1213 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
1214 "");
1215
1216 if (!inventoryPath.empty())
1217 {
1218 types::ObjectMap l_pimObjMap{
1219 {inventoryPath,
1220 {{constants::kwdVpdInf,
1221 {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
1222
1223 if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
1224 {
1225 logging::logMessage(
1226 "Call to PIM failed for file " + i_vpdFilePath);
1227 }
1228 }
1229 else
1230 {
1231 logging::logMessage(
1232 "Inventory path is empty in Json for file " + i_vpdFilePath);
1233 }
1234
1235 return false;
1236 }
1237 return true;
1238}
1239
1240bool Worker::processPostAction(
1241 const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
1242 const std::optional<types::VPDMapVariant> i_parsedVpd)
1243{
1244 if (i_vpdFruPath.empty() || i_flagToProcess.empty())
1245 {
1246 logging::logMessage(
1247 "Invalid input parameter. Abort processing post action");
1248 return false;
1249 }
1250
1251 // Check if post action tag is to be triggered in the flow of collection
1252 // based on some CCIN value?
1253 if (m_parsedJson["frus"][i_vpdFruPath]
1254 .at(0)["postAction"][i_flagToProcess]
1255 .contains("ccin"))
1256 {
1257 if (!i_parsedVpd.has_value())
1258 {
1259 logging::logMessage("Empty VPD Map");
1260 return false;
1261 }
1262
1263 // CCIN match is required to process post action for this FRU as it
1264 // contains the flag.
1265 if (!vpdSpecificUtility::findCcinInVpd(
1266 m_parsedJson["frus"][i_vpdFruPath].at(
1267 0)["postAction"]["collection"],
1268 i_parsedVpd.value()))
1269 {
1270 // If CCIN is not found, implies post action processing is not
1271 // required for this FRU. Let the flow continue.
1272 return true;
1273 }
1274 }
1275
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001276 uint16_t l_errCode = 0;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001277 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001278 i_vpdFruPath, i_flagToProcess,
1279 l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001280 {
1281 logging::logMessage(
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001282 "Execution of post action failed for path: " + i_vpdFruPath +
1283 " . Reason: " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001284
1285 // If post action was required and failed only in that case return
1286 // false. In all other case post action is considered passed.
1287 return false;
1288 }
1289
1290 return true;
1291}
1292
1293types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
1294{
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001295 try
1296 {
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001297 uint16_t l_errCode = 0;
1298
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301299 if (i_vpdFilePath.empty())
1300 {
1301 throw std::runtime_error(
1302 std::string(__FUNCTION__) +
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301303 " Empty VPD file path passed. Abort processing");
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301304 }
1305
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301306 bool isPreActionRequired = false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001307 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001308 "preAction", "collection", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001309 {
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001310 l_errCode = 0;
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301311 isPreActionRequired = true;
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001312 if (!processPreAction(i_vpdFilePath, "collection", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001313 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001314 if (l_errCode == error_code::DEVICE_NOT_PRESENT)
1315 {
1316 logging::logMessage(
1317 vpdSpecificUtility::getErrCodeMsg(l_errCode) +
1318 i_vpdFilePath);
1319 // Presence pin has been read successfully and has been read
1320 // as false, so this is not a failure case, hence returning
1321 // empty variant so that pre action is not marked as failed.
1322 return types::VPDMapVariant{};
1323 }
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301324 throw std::runtime_error(
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001325 std::string(__FUNCTION__) +
1326 " Pre-Action failed with error: " +
1327 vpdSpecificUtility::getErrCodeMsg(l_errCode));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001328 }
1329 }
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001330 else if (l_errCode)
1331 {
1332 logging::logMessage(
1333 "Failed to check if pre action required for FRU [" +
1334 i_vpdFilePath +
1335 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
1336 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001337
1338 if (!std::filesystem::exists(i_vpdFilePath))
1339 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301340 if (isPreActionRequired)
1341 {
1342 throw std::runtime_error(
1343 std::string(__FUNCTION__) + " Could not find file path " +
1344 i_vpdFilePath + "Skipping parser trigger for the EEPROM");
1345 }
1346 return types::VPDMapVariant{};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001347 }
1348
1349 std::shared_ptr<Parser> vpdParser =
1350 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
1351
1352 types::VPDMapVariant l_parsedVpd = vpdParser->parse();
1353
1354 // Before returning, as collection is over, check if FRU qualifies for
1355 // any post action in the flow of collection.
1356 // Note: Don't change the order, post action needs to be processed only
1357 // after collection for FRU is successfully done.
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001358 l_errCode = 0;
1359
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001360 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001361 "postAction", "collection",
1362 l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001363 {
1364 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
1365 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301366 // Post action was required but failed while executing.
1367 // Behaviour can be undefined.
1368 EventLogger::createSyncPel(
1369 types::ErrorType::InternalFailure,
1370 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
1371 std::string("Required post action failed for path [" +
1372 i_vpdFilePath + "]"),
1373 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001374 }
1375 }
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001376 else if (l_errCode)
1377 {
1378 logging::logMessage(
1379 "Error while checking if post action required for FRU [" +
1380 i_vpdFilePath +
1381 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
1382 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001383
1384 return l_parsedVpd;
1385 }
1386 catch (std::exception& l_ex)
1387 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001388 uint16_t l_errCode = 0;
Souvik Roy37c6bef2025-07-17 00:55:59 -05001389 std::string l_exMsg{
1390 std::string(__FUNCTION__) + " : VPD parsing failed for " +
1391 i_vpdFilePath + " due to error: " + l_ex.what()};
1392
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001393 // If post fail action is required, execute it.
1394 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001395 "postFailAction", "collection",
1396 l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001397 {
1398 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001399 "collection", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001400 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001401 l_exMsg += ". Post fail action also failed. Error : " +
1402 vpdSpecificUtility::getErrCodeMsg(l_errCode) +
1403 " Aborting collection for this FRU.";
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001404 }
1405 }
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001406 else if (l_errCode)
1407 {
1408 l_exMsg +=
1409 ". Failed to check if post fail action required, error : " +
1410 vpdSpecificUtility::getErrCodeMsg(l_errCode);
1411 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001412
Souvik Roy37c6bef2025-07-17 00:55:59 -05001413 if (typeid(l_ex) == typeid(DataException))
1414 {
1415 throw DataException(l_exMsg);
1416 }
1417 else if (typeid(l_ex) == typeid(EccException))
1418 {
1419 throw EccException(l_exMsg);
1420 }
1421 throw std::runtime_error(l_exMsg);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001422 }
1423}
1424
Patrick Williams43fedab2025-02-03 14:28:05 -05001425std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1426 const std::string& i_vpdFilePath)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001427{
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001428 std::string l_inventoryPath{};
1429
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001430 try
1431 {
1432 m_semaphore.acquire();
1433
1434 // Thread launched.
1435 m_mutex.lock();
1436 m_activeCollectionThreadCount++;
1437 m_mutex.unlock();
1438
Anupama B R4c65fcd2025-09-01 08:09:00 -05001439 setCollectionStatusProperty(i_vpdFilePath,
1440 constants::vpdCollectionInProgress);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001441
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001442 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301443 if (!std::holds_alternative<std::monostate>(parsedVpdMap))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001444 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301445 types::ObjectMap objectInterfaceMap;
1446 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1447
1448 // Notify PIM
1449 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1450 {
1451 throw std::runtime_error(
1452 std::string(__FUNCTION__) +
1453 "Call to PIM failed while publishing VPD.");
1454 }
1455 }
1456 else
1457 {
1458 logging::logMessage("Empty parsedVpdMap recieved for path [" +
1459 i_vpdFilePath + "]. Check PEL for reason.");
Anupama B R4c65fcd2025-09-01 08:09:00 -05001460
1461 // As empty parsedVpdMap recieved for some reason, but still
1462 // considered VPD collection is completed. Hence FRU collection
1463 // Status will be set as completed.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001464 }
1465 }
1466 catch (const std::exception& ex)
1467 {
Anupama B R24691d22025-05-21 08:14:15 -05001468 setCollectionStatusProperty(i_vpdFilePath,
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001469 constants::vpdCollectionFailed);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001470
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001471 // handle all the exceptions internally. Return only true/false
1472 // based on status of execution.
1473 if (typeid(ex) == std::type_index(typeid(DataException)))
1474 {
Rekha Aparna017567a2025-08-13 02:07:06 -05001475 uint16_t l_errCode = 0;
Sunny Srivastava78c91072025-02-05 14:09:50 +05301476 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1477 // logging error for these cases.
1478 if (vpdSpecificUtility::isPass1Planar())
1479 {
Rekha Aparna017567a2025-08-13 02:07:06 -05001480 std::string l_invPath =
1481 jsonUtility::getInventoryObjPathFromJson(
1482 m_parsedJson, i_vpdFilePath, l_errCode);
1483
1484 if (l_errCode != 0)
1485 {
1486 logging::logMessage(
1487 "Failed to get inventory object path from JSON for FRU [" +
1488 i_vpdFilePath + "], error: " +
1489 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1490 }
1491
RekhaAparna011ef21002025-02-18 23:47:36 -06001492 const std::string& l_invPathLeafValue =
Rekha Aparna017567a2025-08-13 02:07:06 -05001493 sdbusplus::message::object_path(l_invPath).filename();
Sunny Srivastava78c91072025-02-05 14:09:50 +05301494
RekhaAparna011ef21002025-02-18 23:47:36 -06001495 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1496 std::string::npos))
Sunny Srivastava78c91072025-02-05 14:09:50 +05301497 {
1498 // skip logging any PEL for PCIe cards on pass 1 planar.
1499 return std::make_tuple(false, i_vpdFilePath);
1500 }
1501 }
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301502 }
Sunny Srivastava78c91072025-02-05 14:09:50 +05301503
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301504 EventLogger::createSyncPel(
Souvik Roy37c6bef2025-07-17 00:55:59 -05001505 EventLogger::getErrorType(ex),
1506 (typeid(ex) == typeid(DataException)) ||
1507 (typeid(ex) == typeid(EccException))
1508 ? types::SeverityType::Warning
1509 : types::SeverityType::Informational,
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301510 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1511 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001512
1513 // TODO: Figure out a way to clear data in case of any failure at
1514 // runtime.
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301515
1516 // set present property to false for any error case. In future this will
1517 // be replaced by presence logic.
Souvik Roy6a9553c2025-02-07 01:16:32 -06001518 // Update Present property for this FRU only if we handle Present
1519 // property for the FRU.
1520 if (isPresentPropertyHandlingRequired(
1521 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1522 {
1523 setPresentProperty(i_vpdFilePath, false);
1524 }
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301525
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001526 m_semaphore.release();
1527 return std::make_tuple(false, i_vpdFilePath);
1528 }
Anupama B R4c65fcd2025-09-01 08:09:00 -05001529
1530 setCollectionStatusProperty(i_vpdFilePath,
1531 constants::vpdCollectionCompleted);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001532 m_semaphore.release();
1533 return std::make_tuple(true, i_vpdFilePath);
1534}
1535
Sunny Srivastava61611752025-02-04 00:29:33 -06001536bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1537{
1538 if (i_vpdFilePath.empty())
1539 {
1540 return true;
1541 }
1542
1543 // skip processing of system VPD again as it has been already collected.
1544 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1545 {
1546 return true;
1547 }
1548
1549 if (dbusUtility::isChassisPowerOn())
1550 {
1551 // If chassis is powered on, skip collecting FRUs which are
1552 // powerOffOnly.
Rekha Aparna52041882025-09-01 20:48:07 -05001553
1554 uint16_t l_errCode = 0;
1555 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath,
1556 l_errCode))
Sunny Srivastava61611752025-02-04 00:29:33 -06001557 {
1558 return true;
1559 }
Rekha Aparna52041882025-09-01 20:48:07 -05001560 else if (l_errCode)
1561 {
1562 logging::logMessage(
1563 "Failed to check if FRU is power off only for FRU [" +
1564 i_vpdFilePath +
1565 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
1566 }
Sunny Srivastava61611752025-02-04 00:29:33 -06001567
Rekha Aparna52041882025-09-01 20:48:07 -05001568 l_errCode = 0;
Rekha Aparna017567a2025-08-13 02:07:06 -05001569 std::string l_invPath = jsonUtility::getInventoryObjPathFromJson(
1570 m_parsedJson, i_vpdFilePath, l_errCode);
1571
1572 if (l_errCode)
1573 {
1574 logging::logMessage(
1575 "Failed to get inventory path from JSON for FRU [" +
1576 i_vpdFilePath +
1577 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
1578
1579 return false;
1580 }
1581
Sunny Srivastava61611752025-02-04 00:29:33 -06001582 const std::string& l_invPathLeafValue =
Rekha Aparna017567a2025-08-13 02:07:06 -05001583 sdbusplus::message::object_path(l_invPath).filename();
Sunny Srivastava61611752025-02-04 00:29:33 -06001584
1585 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1586 {
1587 return true;
1588 }
1589 }
1590
1591 return false;
1592}
1593
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001594void Worker::collectFrusFromJson()
1595{
1596 // A parsed JSON file should be present to pick FRUs EEPROM paths
1597 if (m_parsedJson.empty())
1598 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301599 throw JsonException(
1600 std::string(__FUNCTION__) +
1601 ": Config JSON is mandatory for processing of FRUs through this API.",
1602 m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001603 }
1604
1605 const nlohmann::json& listOfFrus =
1606 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1607
1608 for (const auto& itemFRUS : listOfFrus.items())
1609 {
1610 const std::string& vpdFilePath = itemFRUS.key();
1611
Sunny Srivastava61611752025-02-04 00:29:33 -06001612 if (skipPathForCollection(vpdFilePath))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001613 {
1614 continue;
1615 }
1616
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001617 try
1618 {
1619 std::thread{[vpdFilePath, this]() {
1620 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001621
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001622 m_mutex.lock();
1623 m_activeCollectionThreadCount--;
1624 m_mutex.unlock();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001625
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001626 if (!m_activeCollectionThreadCount)
1627 {
1628 m_isAllFruCollected = true;
1629 }
1630 }}.detach();
1631 }
1632 catch (const std::exception& l_ex)
1633 {
1634 // add vpdFilePath(EEPROM path) to failed list
1635 m_failedEepromPaths.push_front(vpdFilePath);
1636 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001637 }
1638}
1639
1640// ToDo: Move the API under IBM_SYSTEM
1641void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1642{
1643 try
1644 {
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001645 uint16_t l_errCode = 0;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001646 std::string l_backupAndRestoreCfgFilePath =
1647 m_parsedJson.value("backupRestoreConfigPath", "");
1648
1649 nlohmann::json l_backupAndRestoreCfgJsonObj =
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001650 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath,
1651 l_errCode);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001652
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001653 if (l_errCode)
RekhaAparna011ef21002025-02-18 23:47:36 -06001654 {
Rekha Aparnaca9a0862025-08-29 04:08:33 -05001655 throw JsonException(
1656 "JSON parsing failed for file [ " +
1657 l_backupAndRestoreCfgFilePath + " ], error : " +
1658 vpdSpecificUtility::getErrCodeMsg(l_errCode),
1659 l_backupAndRestoreCfgFilePath);
RekhaAparna011ef21002025-02-18 23:47:36 -06001660 }
1661
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001662 // check if either of "source" or "destination" has inventory path.
1663 // this indicates that this sytem has System VPD on hardware
1664 // and other copy on D-Bus (BMC cache).
1665 if (!l_backupAndRestoreCfgJsonObj.empty() &&
1666 ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1667 l_backupAndRestoreCfgJsonObj["source"].contains(
1668 "inventoryPath")) ||
1669 (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1670 l_backupAndRestoreCfgJsonObj["destination"].contains(
1671 "inventoryPath"))))
1672 {
1673 BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1674 auto [l_srcVpdVariant,
1675 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1676
1677 // ToDo: Revisit is this check is required or not.
1678 if (auto l_srcVpdMap =
1679 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1680 l_srcVpdMap && !(*l_srcVpdMap).empty())
1681 {
1682 io_srcVpdMap = std::move(l_srcVpdVariant);
1683 }
1684 }
1685 }
1686 catch (const std::exception& l_ex)
1687 {
1688 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301689 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301690 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001691 std::string(
1692 "Exception caught while backup and restore VPD keyword's.") +
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301693 EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001694 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1695 }
1696}
1697
1698void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1699{
1700 if (i_dbusObjPath.empty())
1701 {
1702 throw std::runtime_error("Given DBus object path is empty.");
1703 }
1704
Rekha Aparna0578dd22025-09-02 08:20:21 -05001705 uint16_t l_errCode = 0;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001706 const std::string& l_fruPath =
Rekha Aparna0578dd22025-09-02 08:20:21 -05001707 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath, l_errCode);
1708
1709 if (l_errCode)
1710 {
1711 logging::logMessage(
1712 "Failed to get FRU path for inventory path [" + i_dbusObjPath +
1713 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode) +
1714 " Aborting FRU VPD deletion.");
1715 return;
1716 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001717
1718 try
1719 {
1720 auto l_presentPropValue = dbusUtility::readDbusProperty(
1721 constants::pimServiceName, i_dbusObjPath,
1722 constants::inventoryItemInf, "Present");
1723
1724 if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1725 {
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001726 uint16_t l_errCode = 0;
Souvik Roye9120152025-07-02 08:24:38 -05001727 // check if FRU's Present property is handled by vpd-manager
1728 const auto& l_isFruPresenceHandled =
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001729 jsonUtility::isFruPresenceHandled(m_parsedJson, l_fruPath,
1730 l_errCode);
1731
1732 if (l_errCode)
1733 {
1734 throw std::runtime_error(
1735 "Failed to check if FRU's presence is handled, reason: " +
1736 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1737 }
Souvik Roye9120152025-07-02 08:24:38 -05001738
1739 if (!(*l_value) && l_isFruPresenceHandled)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001740 {
1741 throw std::runtime_error("Given FRU is not present");
1742 }
Souvik Roye9120152025-07-02 08:24:38 -05001743 else if (*l_value && !l_isFruPresenceHandled)
1744 {
1745 throw std::runtime_error(
1746 "Given FRU is present and its presence is not handled by vpd-manager.");
1747 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001748 else
1749 {
1750 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001751 "preAction", "deletion",
1752 l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001753 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001754 if (!processPreAction(l_fruPath, "deletion", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001755 {
Sunny Srivastava4f053df2025-09-03 02:27:37 -05001756 std::string l_msg = "Pre action failed";
1757 if (l_errCode)
1758 {
1759 l_msg +=
1760 " Reason: " +
1761 vpdSpecificUtility::getErrCodeMsg(l_errCode);
1762 }
1763 throw std::runtime_error(l_msg);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001764 }
1765 }
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001766 else if (l_errCode)
1767 {
1768 logging::logMessage(
1769 "Failed to check if pre action required for FRU [" +
1770 l_fruPath + "], error : " +
1771 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1772 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001773
1774 std::vector<std::string> l_interfaceList{
1775 constants::operationalStatusInf};
1776
1777 types::MapperGetSubTree l_subTreeMap =
1778 dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1779 l_interfaceList);
1780
1781 types::ObjectMap l_objectMap;
1782
1783 // Updates VPD specific interfaces property value under PIM for
1784 // sub FRUs.
1785 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1786 l_subTreeMap)
1787 {
1788 types::InterfaceMap l_interfaceMap;
1789 vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1790 l_interfaceMap);
1791 l_objectMap.emplace(l_objectPath,
1792 std::move(l_interfaceMap));
1793 }
1794
1795 types::InterfaceMap l_interfaceMap;
1796 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1797 l_interfaceMap);
1798
1799 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1800
1801 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1802 {
1803 throw std::runtime_error("Call to PIM failed.");
1804 }
1805
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001806 l_errCode = 0;
1807
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001808 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001809 "postAction", "deletion",
1810 l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001811 {
1812 if (!processPostAction(l_fruPath, "deletion"))
1813 {
1814 throw std::runtime_error("Post action failed");
1815 }
1816 }
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001817 else if (l_errCode)
1818 {
1819 logging::logMessage(
1820 "Failed to check if post action required during deletion for FRU [" +
1821 l_fruPath + "], error : " +
1822 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1823 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001824 }
1825 }
1826 else
1827 {
1828 logging::logMessage(
1829 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1830 "] as unable to read present property");
1831 return;
1832 }
1833
1834 logging::logMessage(
1835 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1836 }
1837 catch (const std::exception& l_ex)
1838 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001839 uint16_t l_errCode = 0;
1840 std::string l_errMsg =
1841 "Failed to delete VPD for FRU : " + i_dbusObjPath +
1842 " error: " + std::string(l_ex.what());
1843
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001844 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001845 "postFailAction", "deletion",
1846 l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001847 {
1848 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001849 "deletion", l_errCode))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001850 {
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001851 l_errMsg += ". Post fail action also failed, error : " +
1852 vpdSpecificUtility::getErrCodeMsg(l_errCode);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001853 }
1854 }
Rekha Aparnab50bf0e2025-09-02 21:13:26 -05001855 else if (l_errCode)
1856 {
1857 l_errMsg +=
1858 ". Failed to check if post fail action required, error : " +
1859 vpdSpecificUtility::getErrCodeMsg(l_errCode);
1860 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001861
Rekha Aparnaff7d7992025-09-01 11:08:53 -05001862 logging::logMessage(l_errMsg);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001863 }
1864}
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301865
1866void Worker::setPresentProperty(const std::string& i_vpdPath,
1867 const bool& i_value)
1868{
1869 try
1870 {
1871 if (i_vpdPath.empty())
1872 {
1873 throw std::runtime_error(
1874 "Path is empty. Can't set present property");
1875 }
1876
1877 types::ObjectMap l_objectInterfaceMap;
1878
1879 // If the given path is EEPROM path.
1880 if (m_parsedJson["frus"].contains(i_vpdPath))
1881 {
1882 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1883 {
1884 sdbusplus::message::object_path l_fruObjectPath(
1885 l_Fru["inventoryPath"]);
1886
1887 types::PropertyMap l_propertyValueMap;
1888 l_propertyValueMap.emplace("Present", i_value);
1889
1890 types::InterfaceMap l_interfaces;
1891 vpdSpecificUtility::insertOrMerge(l_interfaces,
1892 constants::inventoryItemInf,
1893 move(l_propertyValueMap));
1894
1895 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1896 std::move(l_interfaces));
1897 }
1898 }
1899 else
1900 {
1901 // consider it as an inventory path.
1902 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1903 {
1904 throw std::runtime_error(
1905 "Invalid inventory path: " + i_vpdPath);
1906 }
1907
1908 types::PropertyMap l_propertyValueMap;
1909 l_propertyValueMap.emplace("Present", i_value);
1910
1911 types::InterfaceMap l_interfaces;
1912 vpdSpecificUtility::insertOrMerge(l_interfaces,
1913 constants::inventoryItemInf,
1914 move(l_propertyValueMap));
1915
1916 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1917 }
1918
1919 // Notify PIM
1920 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1921 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301922 throw DbusException(
1923 std::string(__FUNCTION__) +
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301924 "Call to PIM failed while setting present property for path " +
1925 i_vpdPath);
1926 }
1927 }
1928 catch (const std::exception& l_ex)
1929 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301930 EventLogger::createSyncPel(
1931 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1932 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1933 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301934 }
1935}
1936
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301937void Worker::performVpdRecollection()
1938{
1939 try
1940 {
1941 // Check if system config JSON is present
1942 if (m_parsedJson.empty())
1943 {
1944 throw std::runtime_error(
1945 "System config json object is empty, can't process recollection.");
1946 }
1947
Rekha Aparna88d53302025-09-01 18:16:55 -05001948 uint16_t l_errCode = 0;
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301949 const auto& l_frusReplaceableAtStandby =
Rekha Aparna88d53302025-09-01 18:16:55 -05001950 jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson,
1951 l_errCode);
1952
1953 if (l_errCode)
1954 {
1955 logging::logMessage(
1956 "Failed to get list of FRUs replaceable at runtime, error : " +
1957 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1958 return;
1959 }
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301960
1961 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1962 {
1963 // ToDo: Add some logic/trace to know the flow to
1964 // collectSingleFruVpd has been directed via
1965 // performVpdRecollection.
1966 collectSingleFruVpd(l_fruInventoryPath);
1967 }
1968 return;
1969 }
1970
1971 catch (const std::exception& l_ex)
1972 {
1973 // TODO Log PEL
1974 logging::logMessage(
1975 "VPD recollection failed with error: " + std::string(l_ex.what()));
1976 }
1977}
1978
1979void Worker::collectSingleFruVpd(
1980 const sdbusplus::message::object_path& i_dbusObjPath)
1981{
Anupama B R48f297b2025-08-13 04:29:06 -05001982 std::string l_fruPath{};
Rekha Aparna0578dd22025-09-02 08:20:21 -05001983 uint16_t l_errCode = 0;
1984
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301985 try
1986 {
1987 // Check if system config JSON is present
1988 if (m_parsedJson.empty())
1989 {
1990 logging::logMessage(
1991 "System config JSON object not present. Single FRU VPD collection is not performed for " +
1992 std::string(i_dbusObjPath));
1993 return;
1994 }
1995
1996 // Get FRU path for the given D-bus object path from JSON
Rekha Aparna0578dd22025-09-02 08:20:21 -05001997 l_fruPath = jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath,
1998 l_errCode);
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301999
2000 if (l_fruPath.empty())
2001 {
Rekha Aparna0578dd22025-09-02 08:20:21 -05002002 if (l_errCode)
2003 {
2004 logging::logMessage(
2005 "Failed to get FRU path for [" +
2006 std::string(i_dbusObjPath) + "], error : " +
2007 vpdSpecificUtility::getErrCodeMsg(l_errCode) +
2008 " Aborting single FRU VPD collection.");
2009 return;
2010 }
2011
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302012 logging::logMessage(
2013 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
2014 std::string(i_dbusObjPath));
2015 return;
2016 }
2017
2018 // Check if host is up and running
2019 if (dbusUtility::isHostRunning())
2020 {
Rekha Aparnaad0db9e2025-09-01 20:29:18 -05002021 uint16_t l_errCode = 0;
2022 bool isFruReplaceableAtRuntime =
2023 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
2024 l_errCode);
2025
2026 if (l_errCode)
2027 {
2028 logging::logMessage(
2029 "Failed to check if FRU is replaceable at runtime for FRU : [" +
2030 std::string(i_dbusObjPath) + "], error : " +
2031 vpdSpecificUtility::getErrCodeMsg(l_errCode));
2032 return;
2033 }
2034
2035 if (!isFruReplaceableAtRuntime)
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302036 {
2037 logging::logMessage(
2038 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
2039 std::string(i_dbusObjPath));
2040 return;
2041 }
2042 }
2043 else if (dbusUtility::isBMCReady())
2044 {
Rekha Aparna40845612025-09-01 19:58:56 -05002045 uint16_t l_errCode = 0;
2046 bool isFruReplaceableAtStandby =
2047 jsonUtility::isFruReplaceableAtStandby(m_parsedJson, l_fruPath,
2048 l_errCode);
2049
2050 if (l_errCode)
2051 {
2052 logging::logMessage(
2053 "Error while checking if FRU is replaceable at standby for FRU [" +
2054 std::string(i_dbusObjPath) + "], error : " +
2055 vpdSpecificUtility::getErrCodeMsg(l_errCode));
2056 }
2057
Rekha Aparnaad0db9e2025-09-01 20:29:18 -05002058 l_errCode = 0;
2059 bool isFruReplaceableAtRuntime =
2060 jsonUtility::isFruReplaceableAtRuntime(m_parsedJson, l_fruPath,
2061 l_errCode);
2062
2063 if (l_errCode)
2064 {
2065 logging::logMessage(
2066 "Failed to check if FRU is replaceable at runtime for FRU : [" +
2067 std::string(i_dbusObjPath) + "], error : " +
2068 vpdSpecificUtility::getErrCodeMsg(l_errCode));
2069 return;
2070 }
2071
2072 if (!isFruReplaceableAtStandby && (!isFruReplaceableAtRuntime))
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302073 {
2074 logging::logMessage(
2075 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
2076 std::string(i_dbusObjPath));
2077 return;
2078 }
2079 }
2080
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002081 // Set collection Status as InProgress. Since it's an intermediate state
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302082 // D-bus set-property call is good enough to update the status.
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002083 const std::string& l_collStatusProp = "Status";
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302084
Anupama B R4c65fcd2025-09-01 08:09:00 -05002085 setCollectionStatusProperty(l_fruPath,
2086 constants::vpdCollectionInProgress);
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302087
2088 // Parse VPD
2089 types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
2090
2091 // If l_parsedVpd is pointing to std::monostate
2092 if (l_parsedVpd.index() == 0)
2093 {
2094 throw std::runtime_error(
2095 "VPD parsing failed for " + std::string(i_dbusObjPath));
2096 }
2097
2098 // Get D-bus object map from worker class
2099 types::ObjectMap l_dbusObjectMap;
2100 populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
2101
2102 if (l_dbusObjectMap.empty())
2103 {
2104 throw std::runtime_error(
2105 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
2106 std::string(i_dbusObjPath));
2107 }
2108
2109 // Call PIM's Notify method
2110 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
2111 {
2112 throw std::runtime_error(
2113 "Notify PIM failed. Single FRU VPD collection failed for " +
2114 std::string(i_dbusObjPath));
2115 }
Anupama B R4c65fcd2025-09-01 08:09:00 -05002116 setCollectionStatusProperty(l_fruPath,
2117 constants::vpdCollectionCompleted);
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302118 }
2119 catch (const std::exception& l_error)
2120 {
Anupama B R48f297b2025-08-13 04:29:06 -05002121 setCollectionStatusProperty(l_fruPath, constants::vpdCollectionFailed);
Sunny Srivastava380efbb2025-04-25 10:28:30 +05302122 // TODO: Log PEL
2123 logging::logMessage(std::string(l_error.what()));
2124 }
2125}
Anupama B R24691d22025-05-21 08:14:15 -05002126
2127void Worker::setCollectionStatusProperty(
2128 const std::string& i_vpdPath, const std::string& i_value) const noexcept
2129{
2130 try
2131 {
2132 if (i_vpdPath.empty())
2133 {
2134 throw std::runtime_error(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002135 "Given path is empty. Can't set collection Status property");
Anupama B R24691d22025-05-21 08:14:15 -05002136 }
2137
Anupama B R4c65fcd2025-09-01 08:09:00 -05002138 types::PropertyMap l_timeStampMap;
2139 if (i_value == constants::vpdCollectionCompleted ||
2140 i_value == constants::vpdCollectionFailed)
2141 {
2142 l_timeStampMap.emplace(
2143 "CompletedTime",
2144 types::DbusVariantType{
2145 commonUtility::getCurrentTimeSinceEpoch()});
2146 }
2147 else if (i_value == constants::vpdCollectionInProgress)
2148 {
2149 l_timeStampMap.emplace(
2150 "StartTime", types::DbusVariantType{
2151 commonUtility::getCurrentTimeSinceEpoch()});
2152 }
2153 else if (i_value == constants::vpdCollectionNotStarted)
2154 {
2155 l_timeStampMap.emplace("StartTime", 0);
2156 l_timeStampMap.emplace("CompletedTime", 0);
2157 }
2158
Anupama B R24691d22025-05-21 08:14:15 -05002159 types::ObjectMap l_objectInterfaceMap;
2160
2161 if (m_parsedJson["frus"].contains(i_vpdPath))
2162 {
2163 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
2164 {
2165 sdbusplus::message::object_path l_fruObjectPath(
2166 l_Fru["inventoryPath"]);
2167
2168 types::PropertyMap l_propertyValueMap;
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002169 l_propertyValueMap.emplace("Status", i_value);
Anupama B R4c65fcd2025-09-01 08:09:00 -05002170 l_propertyValueMap.insert(l_timeStampMap.begin(),
2171 l_timeStampMap.end());
Anupama B R24691d22025-05-21 08:14:15 -05002172
2173 types::InterfaceMap l_interfaces;
2174 vpdSpecificUtility::insertOrMerge(
2175 l_interfaces, constants::vpdCollectionInterface,
2176 move(l_propertyValueMap));
2177
2178 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
2179 std::move(l_interfaces));
2180 }
2181 }
2182 else
2183 {
2184 // consider it as an inventory path.
2185 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
2186 {
2187 throw std::runtime_error(
2188 "Invalid inventory path: " + i_vpdPath +
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002189 ". Can't set collection Status property");
Anupama B R24691d22025-05-21 08:14:15 -05002190 }
2191
2192 types::PropertyMap l_propertyValueMap;
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002193 l_propertyValueMap.emplace("Status", i_value);
Anupama B R4c65fcd2025-09-01 08:09:00 -05002194 l_propertyValueMap.insert(l_timeStampMap.begin(),
2195 l_timeStampMap.end());
Anupama B R24691d22025-05-21 08:14:15 -05002196
2197 types::InterfaceMap l_interfaces;
2198 vpdSpecificUtility::insertOrMerge(l_interfaces,
2199 constants::vpdCollectionInterface,
2200 move(l_propertyValueMap));
2201
2202 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
2203 }
2204
2205 // Notify PIM
2206 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
2207 {
2208 throw DbusException(
2209 std::string(__FUNCTION__) +
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002210 "Call to PIM failed while setting collection Status property for path " +
Anupama B R24691d22025-05-21 08:14:15 -05002211 i_vpdPath);
2212 }
2213 }
2214 catch (const std::exception& l_ex)
2215 {
2216 EventLogger::createSyncPel(
2217 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
2218 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
2219 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
2220 }
2221}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05002222} // namespace vpd