blob: 63989dce229355cebd09ab539cf3a2f489640a9a [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 {
45 m_parsedJson = jsonUtility::getParsedJson(m_configJsonPath);
46
47 // check for mandatory fields at this point itself.
48 if (!m_parsedJson.contains("frus"))
49 {
50 throw std::runtime_error("Mandatory tag(s) missing from JSON");
51 }
52 }
53 catch (const std::exception& ex)
54 {
55 throw(JsonException(ex.what(), m_configJsonPath));
56 }
57 }
58 else
59 {
60 logging::logMessage("Processing in not based on any config JSON");
61 }
62}
63
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050064static std::string readFitConfigValue()
65{
66 std::vector<std::string> output =
67 commonUtility::executeCmd("/sbin/fw_printenv");
68 std::string fitConfigValue;
69
70 for (const auto& entry : output)
71 {
72 auto pos = entry.find("=");
73 auto key = entry.substr(0, pos);
74 if (key != "fitconfig")
75 {
76 continue;
77 }
78
79 if (pos + 1 < entry.size())
80 {
81 fitConfigValue = entry.substr(pos + 1);
82 }
83 }
84
85 return fitConfigValue;
86}
87
88bool Worker::isSystemVPDOnDBus() const
89{
90 const std::string& mboardPath =
91 m_parsedJson["frus"][SYSTEM_VPD_FILE_PATH].at(0).value(
92 "inventoryPath", "");
93
94 if (mboardPath.empty())
95 {
96 throw JsonException("System vpd file path missing in JSON",
97 INVENTORY_JSON_SYM_LINK);
98 }
99
100 std::array<const char*, 1> interfaces = {
101 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
102
103 const types::MapperGetObject& objectMap =
104 dbusUtility::getObjectMap(mboardPath, interfaces);
105
106 if (objectMap.empty())
107 {
108 return false;
109 }
110 return true;
111}
112
113std::string Worker::getIMValue(const types::IPZVpdMap& parsedVpd) const
114{
115 if (parsedVpd.empty())
116 {
117 throw std::runtime_error("Empty VPD map. Can't Extract IM value");
118 }
119
120 const auto& itrToVSBP = parsedVpd.find("VSBP");
121 if (itrToVSBP == parsedVpd.end())
122 {
123 throw DataException("VSBP record missing.");
124 }
125
126 const auto& itrToIM = (itrToVSBP->second).find("IM");
127 if (itrToIM == (itrToVSBP->second).end())
128 {
129 throw DataException("IM keyword missing.");
130 }
131
132 types::BinaryVector imVal;
133 std::copy(itrToIM->second.begin(), itrToIM->second.end(),
134 back_inserter(imVal));
135
136 std::ostringstream imData;
137 for (auto& aByte : imVal)
138 {
139 imData << std::setw(2) << std::setfill('0') << std::hex
140 << static_cast<int>(aByte);
141 }
142
143 return imData.str();
144}
145
146std::string Worker::getHWVersion(const types::IPZVpdMap& parsedVpd) const
147{
148 if (parsedVpd.empty())
149 {
150 throw std::runtime_error("Empty VPD map. Can't Extract HW value");
151 }
152
153 const auto& itrToVINI = parsedVpd.find("VINI");
154 if (itrToVINI == parsedVpd.end())
155 {
156 throw DataException("VINI record missing.");
157 }
158
159 const auto& itrToHW = (itrToVINI->second).find("HW");
160 if (itrToHW == (itrToVINI->second).end())
161 {
162 throw DataException("HW keyword missing.");
163 }
164
165 types::BinaryVector hwVal;
166 std::copy(itrToHW->second.begin(), itrToHW->second.end(),
167 back_inserter(hwVal));
168
169 // The planar pass only comes from the LSB of the HW keyword,
170 // where as the MSB is used for other purposes such as signifying clock
171 // termination.
172 hwVal[0] = 0x00;
173
174 std::ostringstream hwString;
175 for (auto& aByte : hwVal)
176 {
177 hwString << std::setw(2) << std::setfill('0') << std::hex
178 << static_cast<int>(aByte);
179 }
180
181 return hwString.str();
182}
183
184void Worker::fillVPDMap(const std::string& vpdFilePath,
185 types::VPDMapVariant& vpdMap)
186{
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500187 if (vpdFilePath.empty())
188 {
189 throw std::runtime_error("Invalid file path passed to fillVPDMap API.");
190 }
191
192 if (!std::filesystem::exists(vpdFilePath))
193 {
194 throw std::runtime_error("Can't Find physical file");
195 }
196
Sunny Srivastava043955d2025-01-21 18:04:49 +0530197 std::shared_ptr<Parser> vpdParser =
198 std::make_shared<Parser>(vpdFilePath, m_parsedJson);
199 vpdMap = vpdParser->parse();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500200}
201
202void Worker::getSystemJson(std::string& systemJson,
203 const types::VPDMapVariant& parsedVpdMap)
204{
205 if (auto pVal = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
206 {
207 std::string hwKWdValue = getHWVersion(*pVal);
208 if (hwKWdValue.empty())
209 {
210 throw DataException("HW value fetched is empty.");
211 }
212
213 const std::string& imKwdValue = getIMValue(*pVal);
214 if (imKwdValue.empty())
215 {
216 throw DataException("IM value fetched is empty.");
217 }
218
219 auto itrToIM = config::systemType.find(imKwdValue);
220 if (itrToIM == config::systemType.end())
221 {
222 throw DataException("IM keyword does not map to any system type");
223 }
224
225 const types::HWVerList hwVersionList = itrToIM->second.second;
226 if (!hwVersionList.empty())
227 {
228 transform(hwKWdValue.begin(), hwKWdValue.end(), hwKWdValue.begin(),
229 ::toupper);
230
231 auto itrToHW =
232 std::find_if(hwVersionList.begin(), hwVersionList.end(),
233 [&hwKWdValue](const auto& aPair) {
234 return aPair.first == hwKWdValue;
235 });
236
237 if (itrToHW != hwVersionList.end())
238 {
239 if (!(*itrToHW).second.empty())
240 {
241 systemJson += (*itrToIM).first + "_" + (*itrToHW).second +
242 ".json";
243 }
244 else
245 {
246 systemJson += (*itrToIM).first + ".json";
247 }
248 return;
249 }
250 }
251 systemJson += itrToIM->second.first + ".json";
252 return;
253 }
254
Sunny Srivastava043955d2025-01-21 18:04:49 +0530255 throw DataException(
256 "Invalid VPD type returned from Parser. Can't get system JSON.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500257}
258
259static void setEnvAndReboot(const std::string& key, const std::string& value)
260{
261 // set env and reboot and break.
262 commonUtility::executeCmd("/sbin/fw_setenv", key, value);
263 logging::logMessage("Rebooting BMC to pick up new device tree");
264
265 // make dbus call to reboot
266 auto bus = sdbusplus::bus::new_default_system();
267 auto method = bus.new_method_call(
268 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
269 "org.freedesktop.systemd1.Manager", "Reboot");
270 bus.call_noreply(method);
271}
272
273void Worker::setJsonSymbolicLink(const std::string& i_systemJson)
274{
275 std::error_code l_ec;
276 l_ec.clear();
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530277
278 // Check if symlink file path exists and if the JSON at this location is a
279 // symlink.
280 if (m_isSymlinkPresent &&
281 std::filesystem::is_symlink(INVENTORY_JSON_SYM_LINK, l_ec))
282 { // Don't care about exception in "is_symlink". Will continue with creation
283 // of symlink.
284
285 const auto& l_symlinkFilePth =
286 std::filesystem::read_symlink(INVENTORY_JSON_SYM_LINK, l_ec);
287
288 if (l_ec)
289 {
290 logging::logMessage(
291 "Can't read existing symlink. Error =" + l_ec.message() +
292 "Trying removal of symlink and creation of new symlink.");
293 }
294
295 // If currently set JSON is the required one. No further processing
296 // required.
297 if (i_systemJson == l_symlinkFilePth)
298 {
299 // Correct symlink already set.
300 return;
301 }
302
303 if (!std::filesystem::remove(INVENTORY_JSON_SYM_LINK, l_ec))
304 {
305 // No point going further. If removal fails for existing symlink,
306 // create will anyways throw.
307 throw std::runtime_error(
308 "Removal of symlink failed with Error = " + l_ec.message() +
309 ". Can't proceed with create_symlink.");
310 }
311 }
312
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500313 if (!std::filesystem::exists(VPD_SYMLIMK_PATH, l_ec))
314 {
315 if (l_ec)
316 {
317 throw std::runtime_error(
318 "File system call to exist failed with error = " +
319 l_ec.message());
320 }
321
322 // implies it is a fresh boot/factory reset.
323 // Create the directory for hosting the symlink
324 if (!std::filesystem::create_directories(VPD_SYMLIMK_PATH, l_ec))
325 {
326 if (l_ec)
327 {
328 throw std::runtime_error(
329 "File system call to create directory failed with error = " +
330 l_ec.message());
331 }
332 }
333 }
334
335 // create a new symlink based on the system
336 std::filesystem::create_symlink(i_systemJson, INVENTORY_JSON_SYM_LINK,
337 l_ec);
338
339 if (l_ec)
340 {
341 throw std::runtime_error(
342 "create_symlink system call failed with error: " + l_ec.message());
343 }
344
345 // If the flow is at this point implies the symlink was not present there.
346 // Considering this as factory reset.
347 m_isFactoryResetDone = true;
348}
349
350void Worker::setDeviceTreeAndJson()
351{
352 // JSON is madatory for processing of this API.
353 if (m_parsedJson.empty())
354 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530355 throw JsonException("System config JSON is empty", m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500356 }
357
358 types::VPDMapVariant parsedVpdMap;
359 fillVPDMap(SYSTEM_VPD_FILE_PATH, parsedVpdMap);
360
361 // Implies it is default JSON.
362 std::string systemJson{JSON_ABSOLUTE_PATH_PREFIX};
363
364 // ToDo: Need to check if INVENTORY_JSON_SYM_LINK pointing to correct system
365 // This is required to support movement from rainier to Blue Ridge on the
366 // fly.
367
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530368 getSystemJson(systemJson, parsedVpdMap);
369
370 if (!systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500371 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530372 throw DataException(
373 "No system JSON found corresponding to IM read from VPD.");
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530374 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500375
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530376 // re-parse the JSON once appropriate JSON has been selected.
377 m_parsedJson = jsonUtility::getParsedJson(systemJson);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500378
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530379 if (m_parsedJson.empty())
380 {
381 throw(JsonException("Json parsing failed", systemJson));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500382 }
383
384 std::string devTreeFromJson;
385 if (m_parsedJson.contains("devTree"))
386 {
387 devTreeFromJson = m_parsedJson["devTree"];
388
389 if (devTreeFromJson.empty())
390 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530391 EventLogger::createSyncPel(
392 types::ErrorType::JsonFailure, types::SeverityType::Error,
393 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500394 "Mandatory value for device tree missing from JSON[" +
Sunny Srivastava043955d2025-01-21 18:04:49 +0530395 systemJson + "]",
396 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500397 }
398 }
399
400 auto fitConfigVal = readFitConfigValue();
401
402 if (devTreeFromJson.empty() ||
403 fitConfigVal.find(devTreeFromJson) != std::string::npos)
404 { // Skipping setting device tree as either devtree info is missing from
405 // Json or it is rightly set.
406
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530407 setJsonSymbolicLink(systemJson);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500408
409 if (isSystemVPDOnDBus() &&
410 jsonUtility::isBackupAndRestoreRequired(m_parsedJson))
411 {
412 performBackupAndRestore(parsedVpdMap);
413 }
414
415 // proceed to publish system VPD.
416 publishSystemVPD(parsedVpdMap);
417 return;
418 }
419
420 setEnvAndReboot("fitconfig", devTreeFromJson);
421 exit(EXIT_SUCCESS);
422}
423
424void Worker::populateIPZVPDpropertyMap(
425 types::InterfaceMap& interfacePropMap,
426 const types::IPZKwdValueMap& keyordValueMap,
427 const std::string& interfaceName)
428{
429 types::PropertyMap propertyValueMap;
430 for (const auto& kwdVal : keyordValueMap)
431 {
432 auto kwd = kwdVal.first;
433
434 if (kwd[0] == '#')
435 {
436 kwd = std::string("PD_") + kwd[1];
437 }
438 else if (isdigit(kwd[0]))
439 {
440 kwd = std::string("N_") + kwd;
441 }
442
443 types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end());
444 propertyValueMap.emplace(move(kwd), move(value));
445 }
446
447 if (!propertyValueMap.empty())
448 {
449 interfacePropMap.emplace(interfaceName, propertyValueMap);
450 }
451}
452
453void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
454 types::InterfaceMap& interfaceMap)
455{
456 for (const auto& kwdValMap : keyordVPDMap)
457 {
458 types::PropertyMap propertyValueMap;
459 auto kwd = kwdValMap.first;
460
461 if (kwd[0] == '#')
462 {
463 kwd = std::string("PD_") + kwd[1];
464 }
465 else if (isdigit(kwd[0]))
466 {
467 kwd = std::string("N_") + kwd;
468 }
469
470 if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second))
471 {
472 types::BinaryVector value((*keywordValue).begin(),
473 (*keywordValue).end());
474 propertyValueMap.emplace(move(kwd), move(value));
475 }
476 else if (auto keywordValue = get_if<std::string>(&kwdValMap.second))
477 {
478 types::BinaryVector value((*keywordValue).begin(),
479 (*keywordValue).end());
480 propertyValueMap.emplace(move(kwd), move(value));
481 }
482 else if (auto keywordValue = get_if<size_t>(&kwdValMap.second))
483 {
484 if (kwd == "MemorySizeInKB")
485 {
486 types::PropertyMap memProp;
487 memProp.emplace(move(kwd), ((*keywordValue)));
488 interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm",
489 move(memProp));
490 continue;
491 }
492 else
493 {
494 logging::logMessage(
495 "Unknown Keyword =" + kwd + " found in keyword VPD map");
496 continue;
497 }
498 }
499 else
500 {
501 logging::logMessage(
502 "Unknown variant type found in keyword VPD map.");
503 continue;
504 }
505
506 if (!propertyValueMap.empty())
507 {
508 vpdSpecificUtility::insertOrMerge(
509 interfaceMap, constants::kwdVpdInf, move(propertyValueMap));
510 }
511 }
512}
513
514void Worker::populateInterfaces(const nlohmann::json& interfaceJson,
515 types::InterfaceMap& interfaceMap,
516 const types::VPDMapVariant& parsedVpdMap)
517{
518 for (const auto& interfacesPropPair : interfaceJson.items())
519 {
520 const std::string& interface = interfacesPropPair.key();
521 types::PropertyMap propertyMap;
522
523 for (const auto& propValuePair : interfacesPropPair.value().items())
524 {
525 const std::string property = propValuePair.key();
526
527 if (propValuePair.value().is_boolean())
528 {
529 propertyMap.emplace(property,
530 propValuePair.value().get<bool>());
531 }
532 else if (propValuePair.value().is_string())
533 {
534 if (property.compare("LocationCode") == 0 &&
535 interface.compare("com.ibm.ipzvpd.Location") == 0)
536 {
537 std::string value =
538 vpdSpecificUtility::getExpandedLocationCode(
539 propValuePair.value().get<std::string>(),
540 parsedVpdMap);
541 propertyMap.emplace(property, value);
542
543 auto l_locCodeProperty = propertyMap;
544 vpdSpecificUtility::insertOrMerge(
545 interfaceMap,
546 std::string(constants::xyzLocationCodeInf),
547 move(l_locCodeProperty));
548 }
549 else
550 {
551 propertyMap.emplace(
552 property, propValuePair.value().get<std::string>());
553 }
554 }
555 else if (propValuePair.value().is_array())
556 {
557 try
558 {
559 propertyMap.emplace(
560 property,
561 propValuePair.value().get<types::BinaryVector>());
562 }
563 catch (const nlohmann::detail::type_error& e)
564 {
565 std::cerr << "Type exception: " << e.what() << "\n";
566 }
567 }
568 else if (propValuePair.value().is_number())
569 {
570 // For now assume the value is a size_t. In the future it would
571 // be nice to come up with a way to get the type from the JSON.
572 propertyMap.emplace(property,
573 propValuePair.value().get<size_t>());
574 }
575 else if (propValuePair.value().is_object())
576 {
577 const std::string& record =
578 propValuePair.value().value("recordName", "");
579 const std::string& keyword =
580 propValuePair.value().value("keywordName", "");
581 const std::string& encoding =
582 propValuePair.value().value("encoding", "");
583
584 if (auto ipzVpdMap =
585 std::get_if<types::IPZVpdMap>(&parsedVpdMap))
586 {
587 if (!record.empty() && !keyword.empty() &&
588 (*ipzVpdMap).count(record) &&
589 (*ipzVpdMap).at(record).count(keyword))
590 {
591 auto encoded = vpdSpecificUtility::encodeKeyword(
592 ((*ipzVpdMap).at(record).at(keyword)), encoding);
593 propertyMap.emplace(property, encoded);
594 }
595 }
596 else if (auto kwdVpdMap =
597 std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
598 {
599 if (!keyword.empty() && (*kwdVpdMap).count(keyword))
600 {
601 if (auto kwValue = std::get_if<types::BinaryVector>(
602 &(*kwdVpdMap).at(keyword)))
603 {
604 auto encodedValue =
605 vpdSpecificUtility::encodeKeyword(
606 std::string((*kwValue).begin(),
607 (*kwValue).end()),
608 encoding);
609
610 propertyMap.emplace(property, encodedValue);
611 }
612 else if (auto kwValue = std::get_if<std::string>(
613 &(*kwdVpdMap).at(keyword)))
614 {
615 auto encodedValue =
616 vpdSpecificUtility::encodeKeyword(
617 std::string((*kwValue).begin(),
618 (*kwValue).end()),
619 encoding);
620
621 propertyMap.emplace(property, encodedValue);
622 }
623 else if (auto uintValue = std::get_if<size_t>(
624 &(*kwdVpdMap).at(keyword)))
625 {
626 propertyMap.emplace(property, *uintValue);
627 }
628 else
629 {
630 logging::logMessage(
631 "Unknown keyword found, Keywrod = " + keyword);
632 }
633 }
634 }
635 }
636 }
637 vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
638 move(propertyMap));
639 }
640}
641
642bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
643{
644 const unsigned char l_io[] = {
645 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
646 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
647
648 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
649 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
650 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
651 // IO.
652 if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
653 constants::SIZE_OF_8EQ_IN_PG) == 0)
654 {
655 return true;
656 }
657
658 // The CPU is not an IO
659 return false;
660}
661
662bool Worker::primeInventory(const std::string& i_vpdFilePath)
663{
664 if (i_vpdFilePath.empty())
665 {
666 logging::logMessage("Empty VPD file path given");
667 return false;
668 }
669
670 if (m_parsedJson.empty())
671 {
672 logging::logMessage("Empty JSON detected for " + i_vpdFilePath);
673 return false;
674 }
675 else if (!m_parsedJson["frus"].contains(i_vpdFilePath))
676 {
677 logging::logMessage("File " + i_vpdFilePath +
678 ", is not found in the system config JSON file.");
679 return false;
680 }
681
682 types::ObjectMap l_objectInterfaceMap;
683 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdFilePath])
684 {
685 types::InterfaceMap l_interfaces;
686 sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
687
688 if (l_Fru.contains("ccin"))
689 {
690 continue;
691 }
692
693 if (l_Fru.contains("noprime") && l_Fru.value("noprime", false))
694 {
695 continue;
696 }
697
Souvik Roy6a9553c2025-02-07 01:16:32 -0600698 // Reset data under PIM for this FRU only if the FRU is not synthesized
699 // and we handle it's Present property.
700 if (isPresentPropertyHandlingRequired(l_Fru))
701 {
702 // Clear data under PIM if already exists.
703 vpdSpecificUtility::resetDataUnderPIM(
704 std::string(l_Fru["inventoryPath"]), l_interfaces);
705 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500706
707 // Add extra interfaces mentioned in the Json config file
708 if (l_Fru.contains("extraInterfaces"))
709 {
710 populateInterfaces(l_Fru["extraInterfaces"], l_interfaces,
711 std::monostate{});
712 }
713
714 types::PropertyMap l_propertyValueMap;
Sunny Srivastavad159bb42025-01-09 11:13:50 +0530715
Souvik Roy6a9553c2025-02-07 01:16:32 -0600716 // Update Present property for this FRU only if we handle Present
717 // property for the FRU.
718 if (isPresentPropertyHandlingRequired(l_Fru))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500719 {
Souvik Roy6a9553c2025-02-07 01:16:32 -0600720 l_propertyValueMap.emplace("Present", false);
721
722 // TODO: Present based on file will be taken care in future.
723 // By default present is set to false for FRU at the time of
724 // priming. Once collection goes through, it will be set to true in
725 // that flow.
726 /*if (std::filesystem::exists(i_vpdFilePath))
727 {
728 l_propertyValueMap["Present"] = true;
729 }*/
730 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500731
732 vpdSpecificUtility::insertOrMerge(l_interfaces,
733 "xyz.openbmc_project.Inventory.Item",
734 move(l_propertyValueMap));
735
736 if (l_Fru.value("inherit", true) &&
737 m_parsedJson.contains("commonInterfaces"))
738 {
739 populateInterfaces(m_parsedJson["commonInterfaces"], l_interfaces,
740 std::monostate{});
741 }
742
743 processFunctionalProperty(l_Fru["inventoryPath"], l_interfaces);
744 processEnabledProperty(l_Fru["inventoryPath"], l_interfaces);
745
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -0600746 // Emplace the default state of FRU VPD collection
747 types::PropertyMap l_fruCollectionProperty = {
Anupama B R5cd1b2d2025-08-05 04:57:40 -0500748 {"Status", constants::vpdCollectionNotStarted}};
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -0600749
750 vpdSpecificUtility::insertOrMerge(l_interfaces,
751 constants::vpdCollectionInterface,
752 std::move(l_fruCollectionProperty));
753
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500754 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
755 std::move(l_interfaces));
756 }
757
758 // Notify PIM
759 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
760 {
761 logging::logMessage("Call to PIM failed for VPD file " + i_vpdFilePath);
762 return false;
763 }
764
765 return true;
766}
767
768void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
769 types::InterfaceMap& interfaces)
770{
771 // embedded property(true or false) says whether the subfru is embedded
772 // into the parent fru (or) not. VPD sets Present property only for
773 // embedded frus. If the subfru is not an embedded FRU, the subfru may
774 // or may not be physically present. Those non embedded frus will always
775 // have Present=false irrespective of its physical presence or absence.
776 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
777 // Present to true for such sub frus.
778 // Eg: ethernet port is embedded into bmc card. So set Present to true
779 // for such sub frus. Also donot populate present property for embedded
780 // subfru which is synthesized. Currently there is no subfru which are
781 // both embedded and synthesized. But still the case is handled here.
782
783 // Check if its required to handle presence for this FRU.
784 if (singleFru.value("handlePresence", true))
785 {
786 types::PropertyMap presProp;
787 presProp.emplace("Present", true);
788 vpdSpecificUtility::insertOrMerge(
789 interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp));
790 }
791}
792
793void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
794 types::InterfaceMap& interfaces,
795 const types::VPDMapVariant& parsedVpdMap)
796{
797 populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
798 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
799 {
800 if (singleFru["extraInterfaces"].contains(
801 "xyz.openbmc_project.Inventory.Item.Cpu"))
802 {
803 auto itrToRec = (*ipzVpdMap).find("CP00");
804 if (itrToRec == (*ipzVpdMap).end())
805 {
806 return;
807 }
808
Souvik Roya55fcca2025-02-19 01:33:58 -0600809 const std::string pgKeywordValue{
810 vpdSpecificUtility::getKwVal(itrToRec->second, "PG")};
811
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500812 if (!pgKeywordValue.empty())
813 {
814 if (isCPUIOGoodOnly(pgKeywordValue))
815 {
816 interfaces["xyz.openbmc_project.Inventory.Item"]
817 ["PrettyName"] = "IO Module";
818 }
819 }
Souvik Roya55fcca2025-02-19 01:33:58 -0600820 else
821 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +0530822 throw DataException(std::string(__FUNCTION__) +
823 "Failed to get value for keyword PG");
Souvik Roya55fcca2025-02-19 01:33:58 -0600824 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500825 }
826 }
827}
828
829void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
830 const types::VPDMapVariant& parsedVpdMap,
831 types::InterfaceMap& interfaces)
832{
833 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
834 {
835 for (const auto& record : singleFru["copyRecords"])
836 {
837 const std::string& recordName = record;
838 if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
839 {
840 populateIPZVPDpropertyMap(interfaces,
841 (*ipzVpdMap).at(recordName),
842 constants::ipzVpdInf + recordName);
843 }
844 }
845 }
846}
847
848void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
849 types::InterfaceMap& interfaces)
850{
851 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
852 {
853 for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
854 {
855 populateIPZVPDpropertyMap(interfaces, kwdValueMap,
856 constants::ipzVpdInf + recordName);
857 }
858 }
859 else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
860 {
861 populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
862 }
863
864 if (m_parsedJson.contains("commonInterfaces"))
865 {
866 populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
867 parsedVpdMap);
868 }
869}
870
871bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
872 const types::VPDMapVariant& parsedVpdMap)
873{
874 if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
875 {
876 auto itrToRec = (*ipzVPDMap).find("VINI");
877 if (itrToRec == (*ipzVPDMap).end())
878 {
879 return false;
880 }
881
Souvik Roya55fcca2025-02-19 01:33:58 -0600882 std::string ccinFromVpd{
883 vpdSpecificUtility::getKwVal(itrToRec->second, "CC")};
884
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500885 if (ccinFromVpd.empty())
886 {
887 return false;
888 }
889
890 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
891 ::toupper);
892
893 std::vector<std::string> ccinList;
894 for (std::string ccin : singleFru["ccin"])
895 {
896 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
897 ccinList.push_back(ccin);
898 }
899
900 if (ccinList.empty())
901 {
902 return false;
903 }
904
905 if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
906 ccinList.end())
907 {
908 return false;
909 }
910 }
911 return true;
912}
913
914void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
915 types::InterfaceMap& io_interfaces)
916{
917 if (!dbusUtility::isChassisPowerOn())
918 {
919 std::array<const char*, 1> l_operationalStatusInf = {
920 constants::operationalStatusInf};
921
922 auto mapperObjectMap = dbusUtility::getObjectMap(
923 i_inventoryObjPath, l_operationalStatusInf);
924
925 // If the object has been found. Check if it is under PIM.
926 if (mapperObjectMap.size() != 0)
927 {
928 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
929 {
930 if (l_serviceName == constants::pimServiceName)
931 {
932 // The object is already under PIM. No need to process
933 // again. Retain the old value.
934 return;
935 }
936 }
937 }
938
939 // Implies value is not there in D-Bus. Populate it with default
940 // value "true".
941 types::PropertyMap l_functionalProp;
942 l_functionalProp.emplace("Functional", true);
943 vpdSpecificUtility::insertOrMerge(io_interfaces,
944 constants::operationalStatusInf,
945 move(l_functionalProp));
946 }
947
948 // if chassis is power on. Functional property should be there on D-Bus.
949 // Don't process.
950 return;
951}
952
953void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
954 types::InterfaceMap& io_interfaces)
955{
956 if (!dbusUtility::isChassisPowerOn())
957 {
958 std::array<const char*, 1> l_enableInf = {constants::enableInf};
959
960 auto mapperObjectMap =
961 dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
962
963 // If the object has been found. Check if it is under PIM.
964 if (mapperObjectMap.size() != 0)
965 {
966 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
967 {
968 if (l_serviceName == constants::pimServiceName)
969 {
970 // The object is already under PIM. No need to process
971 // again. Retain the old value.
972 return;
973 }
974 }
975 }
976
977 // Implies value is not there in D-Bus. Populate it with default
978 // value "true".
979 types::PropertyMap l_enabledProp;
980 l_enabledProp.emplace("Enabled", true);
981 vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
982 move(l_enabledProp));
983 }
984
985 // if chassis is power on. Enabled property should be there on D-Bus.
986 // Don't process.
987 return;
988}
989
990void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
991 types::ObjectMap& objectInterfaceMap,
992 const std::string& vpdFilePath)
993{
994 if (vpdFilePath.empty())
995 {
996 throw std::runtime_error(
Sunny Srivastava4c509c22025-03-25 12:43:40 +0530997 std::string(__FUNCTION__) +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500998 "Invalid parameter passed to populateDbus API.");
999 }
1000
1001 // JSON config is mandatory for processing of "if". Add "else" for any
1002 // processing without config JSON.
1003 if (!m_parsedJson.empty())
1004 {
1005 types::InterfaceMap interfaces;
1006
1007 for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
1008 {
1009 const auto& inventoryPath = aFru["inventoryPath"];
1010 sdbusplus::message::object_path fruObjectPath(inventoryPath);
1011 if (aFru.contains("ccin"))
1012 {
1013 if (!processFruWithCCIN(aFru, parsedVpdMap))
1014 {
1015 continue;
1016 }
1017 }
1018
1019 if (aFru.value("inherit", true))
1020 {
1021 processInheritFlag(parsedVpdMap, interfaces);
1022 }
1023
1024 // If specific record needs to be copied.
1025 if (aFru.contains("copyRecords"))
1026 {
1027 processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
1028 }
1029
1030 if (aFru.contains("extraInterfaces"))
1031 {
1032 // Process extra interfaces w.r.t a FRU.
1033 processExtraInterfaces(aFru, interfaces, parsedVpdMap);
1034 }
1035
1036 // Process FRUS which are embedded in the parent FRU and whose VPD
1037 // will be synthesized.
1038 if ((aFru.value("embedded", true)) &&
1039 (!aFru.value("synthesized", false)))
1040 {
1041 processEmbeddedAndSynthesizedFrus(aFru, interfaces);
1042 }
1043
1044 processFunctionalProperty(inventoryPath, interfaces);
1045 processEnabledProperty(inventoryPath, interfaces);
1046
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001047 // Update collection status as successful
1048 types::PropertyMap l_collectionProperty = {
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001049 {"Status", constants::vpdCollectionCompleted}};
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001050
1051 vpdSpecificUtility::insertOrMerge(interfaces,
1052 constants::vpdCollectionInterface,
1053 std::move(l_collectionProperty));
1054
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001055 objectInterfaceMap.emplace(std::move(fruObjectPath),
1056 std::move(interfaces));
1057 }
1058 }
1059}
1060
Patrick Williams43fedab2025-02-03 14:28:05 -05001061std::string Worker::createAssetTagString(
1062 const types::VPDMapVariant& i_parsedVpdMap)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001063{
1064 std::string l_assetTag;
1065
1066 // system VPD will be in IPZ format.
1067 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
1068 {
1069 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
1070 if (l_itrToVsys != (*l_parsedVpdMap).end())
1071 {
Souvik Roya55fcca2025-02-19 01:33:58 -06001072 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal(
1073 l_itrToVsys->second, constants::kwdTM)};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001074
Souvik Roya55fcca2025-02-19 01:33:58 -06001075 if (l_tmKwdValue.empty())
1076 {
1077 throw std::runtime_error(
1078 std::string("Failed to get value for keyword [") +
1079 constants::kwdTM +
1080 std::string("] while creating Asset tag."));
1081 }
1082
1083 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal(
1084 l_itrToVsys->second, constants::kwdSE)};
1085
1086 if (l_seKwdValue.empty())
1087 {
1088 throw std::runtime_error(
1089 std::string("Failed to get value for keyword [") +
1090 constants::kwdSE +
1091 std::string("] while creating Asset tag."));
1092 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001093
1094 l_assetTag = std::string{"Server-"} + l_tmKwdValue +
1095 std::string{"-"} + l_seKwdValue;
1096 }
1097 else
1098 {
1099 throw std::runtime_error(
1100 "VSYS record not found in parsed VPD map to create Asset tag.");
1101 }
1102 }
1103 else
1104 {
1105 throw std::runtime_error(
1106 "Invalid VPD type recieved to create Asset tag.");
1107 }
1108
1109 return l_assetTag;
1110}
1111
1112void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
1113{
1114 types::ObjectMap objectInterfaceMap;
1115
1116 if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
1117 {
1118 populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
1119
1120 try
1121 {
1122 if (m_isFactoryResetDone)
1123 {
1124 const auto& l_assetTag = createAssetTagString(parsedVpdMap);
1125
1126 auto l_itrToSystemPath = objectInterfaceMap.find(
1127 sdbusplus::message::object_path(constants::systemInvPath));
1128 if (l_itrToSystemPath == objectInterfaceMap.end())
1129 {
1130 throw std::runtime_error(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301131 "Asset tag update failed. System Path not found in object map.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001132 }
1133
1134 types::PropertyMap l_assetTagProperty;
1135 l_assetTagProperty.emplace("AssetTag", l_assetTag);
1136
1137 (l_itrToSystemPath->second)
1138 .emplace(constants::assetTagInf,
1139 std::move(l_assetTagProperty));
1140 }
1141 }
1142 catch (const std::exception& l_ex)
1143 {
1144 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301145 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1146 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001147 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1148 }
1149
1150 // Notify PIM
1151 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1152 {
1153 throw std::runtime_error("Call to PIM failed for system VPD");
1154 }
1155 }
1156 else
1157 {
1158 throw DataException("Invalid format of parsed VPD map.");
1159 }
1160}
1161
1162bool Worker::processPreAction(const std::string& i_vpdFilePath,
1163 const std::string& i_flagToProcess)
1164{
1165 if (i_vpdFilePath.empty() || i_flagToProcess.empty())
1166 {
1167 logging::logMessage(
1168 "Invalid input parameter. Abort processing pre action");
1169 return false;
1170 }
1171
1172 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
1173 i_vpdFilePath, i_flagToProcess)) &&
1174 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
1175 {
1176 // TODO: Need a way to delete inventory object from Dbus and persisted
1177 // data section in case any FRU is not present or there is any
1178 // problem in collecting it. Once it has been deleted, it can be
1179 // re-created in the flow of priming the inventory. This needs to be
1180 // done either here or in the exception section of "parseAndPublishVPD"
1181 // API. Any failure in the process of collecting FRU will land up in the
1182 // excpetion of "parseAndPublishVPD".
1183
1184 // If the FRU is not there, clear the VINI/CCIN data.
1185 // Enity manager probes for this keyword to look for this
1186 // FRU, now if the data is persistent on BMC and FRU is
1187 // removed this can lead to ambiguity. Hence clearing this
1188 // Keyword if FRU is absent.
1189 const auto& inventoryPath =
1190 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
1191 "");
1192
1193 if (!inventoryPath.empty())
1194 {
1195 types::ObjectMap l_pimObjMap{
1196 {inventoryPath,
1197 {{constants::kwdVpdInf,
1198 {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
1199
1200 if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
1201 {
1202 logging::logMessage(
1203 "Call to PIM failed for file " + i_vpdFilePath);
1204 }
1205 }
1206 else
1207 {
1208 logging::logMessage(
1209 "Inventory path is empty in Json for file " + i_vpdFilePath);
1210 }
1211
1212 return false;
1213 }
1214 return true;
1215}
1216
1217bool Worker::processPostAction(
1218 const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
1219 const std::optional<types::VPDMapVariant> i_parsedVpd)
1220{
1221 if (i_vpdFruPath.empty() || i_flagToProcess.empty())
1222 {
1223 logging::logMessage(
1224 "Invalid input parameter. Abort processing post action");
1225 return false;
1226 }
1227
1228 // Check if post action tag is to be triggered in the flow of collection
1229 // based on some CCIN value?
1230 if (m_parsedJson["frus"][i_vpdFruPath]
1231 .at(0)["postAction"][i_flagToProcess]
1232 .contains("ccin"))
1233 {
1234 if (!i_parsedVpd.has_value())
1235 {
1236 logging::logMessage("Empty VPD Map");
1237 return false;
1238 }
1239
1240 // CCIN match is required to process post action for this FRU as it
1241 // contains the flag.
1242 if (!vpdSpecificUtility::findCcinInVpd(
1243 m_parsedJson["frus"][i_vpdFruPath].at(
1244 0)["postAction"]["collection"],
1245 i_parsedVpd.value()))
1246 {
1247 // If CCIN is not found, implies post action processing is not
1248 // required for this FRU. Let the flow continue.
1249 return true;
1250 }
1251 }
1252
1253 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
1254 i_vpdFruPath, i_flagToProcess))
1255 {
1256 logging::logMessage(
1257 "Execution of post action failed for path: " + i_vpdFruPath);
1258
1259 // If post action was required and failed only in that case return
1260 // false. In all other case post action is considered passed.
1261 return false;
1262 }
1263
1264 return true;
1265}
1266
1267types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
1268{
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001269 try
1270 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301271 if (i_vpdFilePath.empty())
1272 {
1273 throw std::runtime_error(
1274 std::string(__FUNCTION__) +
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301275 " Empty VPD file path passed. Abort processing");
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301276 }
1277
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301278 bool isPreActionRequired = false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001279 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1280 "preAction", "collection"))
1281 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301282 isPreActionRequired = true;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001283 if (!processPreAction(i_vpdFilePath, "collection"))
1284 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301285 throw std::runtime_error(
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301286 std::string(__FUNCTION__) + " Pre-Action failed");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001287 }
1288 }
1289
1290 if (!std::filesystem::exists(i_vpdFilePath))
1291 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301292 if (isPreActionRequired)
1293 {
1294 throw std::runtime_error(
1295 std::string(__FUNCTION__) + " Could not find file path " +
1296 i_vpdFilePath + "Skipping parser trigger for the EEPROM");
1297 }
1298 return types::VPDMapVariant{};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001299 }
1300
1301 std::shared_ptr<Parser> vpdParser =
1302 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
1303
1304 types::VPDMapVariant l_parsedVpd = vpdParser->parse();
1305
1306 // Before returning, as collection is over, check if FRU qualifies for
1307 // any post action in the flow of collection.
1308 // Note: Don't change the order, post action needs to be processed only
1309 // after collection for FRU is successfully done.
1310 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1311 "postAction", "collection"))
1312 {
1313 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
1314 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301315 // Post action was required but failed while executing.
1316 // Behaviour can be undefined.
1317 EventLogger::createSyncPel(
1318 types::ErrorType::InternalFailure,
1319 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
1320 std::string("Required post action failed for path [" +
1321 i_vpdFilePath + "]"),
1322 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001323 }
1324 }
1325
1326 return l_parsedVpd;
1327 }
1328 catch (std::exception& l_ex)
1329 {
Souvik Roy37c6bef2025-07-17 00:55:59 -05001330 std::string l_exMsg{
1331 std::string(__FUNCTION__) + " : VPD parsing failed for " +
1332 i_vpdFilePath + " due to error: " + l_ex.what()};
1333
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001334 // If post fail action is required, execute it.
1335 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
Sunny Srivastava4c164382025-01-28 03:17:33 -06001336 "postFailAction", "collection"))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001337 {
1338 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
1339 "collection"))
1340 {
Souvik Roy37c6bef2025-07-17 00:55:59 -05001341 l_exMsg +=
1342 ". Post Fail Action also failed, aborting collection for this FRU";
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001343 }
1344 }
1345
Souvik Roy37c6bef2025-07-17 00:55:59 -05001346 if (typeid(l_ex) == typeid(DataException))
1347 {
1348 throw DataException(l_exMsg);
1349 }
1350 else if (typeid(l_ex) == typeid(EccException))
1351 {
1352 throw EccException(l_exMsg);
1353 }
1354 throw std::runtime_error(l_exMsg);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001355 }
1356}
1357
Patrick Williams43fedab2025-02-03 14:28:05 -05001358std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1359 const std::string& i_vpdFilePath)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001360{
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001361 std::string l_inventoryPath{};
1362
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001363 try
1364 {
1365 m_semaphore.acquire();
1366
1367 // Thread launched.
1368 m_mutex.lock();
1369 m_activeCollectionThreadCount++;
1370 m_mutex.unlock();
1371
Rekha Aparna017567a2025-08-13 02:07:06 -05001372 uint16_t l_errCode = 0;
1373
1374 // Set CollectionStatus as InProgress. Since it's an intermediate state
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001375 // D-bus set-property call is good enough to update the status.
RekhaAparna011ef21002025-02-18 23:47:36 -06001376 l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
Rekha Aparna017567a2025-08-13 02:07:06 -05001377 m_parsedJson, i_vpdFilePath, l_errCode);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001378
RekhaAparna011ef21002025-02-18 23:47:36 -06001379 if (!l_inventoryPath.empty())
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001380 {
Sunny Srivastava995e1c22025-08-28 03:13:00 -05001381 // save time stamp under PIM.
1382 vpdSpecificUtility::saveTimeStampInPim(l_inventoryPath,
1383 "StartTime");
1384
RekhaAparna01c532b182025-02-19 19:56:49 -06001385 if (!dbusUtility::writeDbusProperty(
RekhaAparna011ef21002025-02-18 23:47:36 -06001386 jsonUtility::getServiceName(m_parsedJson, l_inventoryPath),
1387 l_inventoryPath, constants::vpdCollectionInterface,
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001388 "Status",
RekhaAparna01c532b182025-02-19 19:56:49 -06001389 types::DbusVariantType{constants::vpdCollectionInProgress}))
RekhaAparna011ef21002025-02-18 23:47:36 -06001390 {
1391 logging::logMessage(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001392 "Unable to set collection Status as InProgress for " +
RekhaAparna01c532b182025-02-19 19:56:49 -06001393 i_vpdFilePath + ". Error : " + "DBus write failed");
RekhaAparna011ef21002025-02-18 23:47:36 -06001394 }
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001395 }
Rekha Aparna017567a2025-08-13 02:07:06 -05001396 else if (l_errCode)
1397 {
1398 logging::logMessage(
1399 "Failed to get inventory path for FRU [" + i_vpdFilePath +
1400 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
1401 }
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001402
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001403 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301404 if (!std::holds_alternative<std::monostate>(parsedVpdMap))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001405 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301406 types::ObjectMap objectInterfaceMap;
1407 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1408
Sunny Srivastava995e1c22025-08-28 03:13:00 -05001409 // save end time stamp under PIM.
1410 vpdSpecificUtility::saveTimeStampInPim(l_inventoryPath,
1411 "CompletedTime");
1412
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301413 // Notify PIM
1414 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1415 {
1416 throw std::runtime_error(
1417 std::string(__FUNCTION__) +
1418 "Call to PIM failed while publishing VPD.");
1419 }
1420 }
1421 else
1422 {
1423 logging::logMessage("Empty parsedVpdMap recieved for path [" +
1424 i_vpdFilePath + "]. Check PEL for reason.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001425 }
1426 }
1427 catch (const std::exception& ex)
1428 {
Anupama B R24691d22025-05-21 08:14:15 -05001429 setCollectionStatusProperty(i_vpdFilePath,
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001430 constants::vpdCollectionFailed);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001431
Sunny Srivastava995e1c22025-08-28 03:13:00 -05001432 // save end time stamp under PIM.
1433 vpdSpecificUtility::saveTimeStampInPim(l_inventoryPath,
1434 "CompletedTime");
1435
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001436 // handle all the exceptions internally. Return only true/false
1437 // based on status of execution.
1438 if (typeid(ex) == std::type_index(typeid(DataException)))
1439 {
Rekha Aparna017567a2025-08-13 02:07:06 -05001440 uint16_t l_errCode = 0;
Sunny Srivastava78c91072025-02-05 14:09:50 +05301441 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1442 // logging error for these cases.
1443 if (vpdSpecificUtility::isPass1Planar())
1444 {
Rekha Aparna017567a2025-08-13 02:07:06 -05001445 std::string l_invPath =
1446 jsonUtility::getInventoryObjPathFromJson(
1447 m_parsedJson, i_vpdFilePath, l_errCode);
1448
1449 if (l_errCode != 0)
1450 {
1451 logging::logMessage(
1452 "Failed to get inventory object path from JSON for FRU [" +
1453 i_vpdFilePath + "], error: " +
1454 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1455 }
1456
RekhaAparna011ef21002025-02-18 23:47:36 -06001457 const std::string& l_invPathLeafValue =
Rekha Aparna017567a2025-08-13 02:07:06 -05001458 sdbusplus::message::object_path(l_invPath).filename();
Sunny Srivastava78c91072025-02-05 14:09:50 +05301459
RekhaAparna011ef21002025-02-18 23:47:36 -06001460 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1461 std::string::npos))
Sunny Srivastava78c91072025-02-05 14:09:50 +05301462 {
1463 // skip logging any PEL for PCIe cards on pass 1 planar.
1464 return std::make_tuple(false, i_vpdFilePath);
1465 }
1466 }
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301467 }
Sunny Srivastava78c91072025-02-05 14:09:50 +05301468
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301469 EventLogger::createSyncPel(
Souvik Roy37c6bef2025-07-17 00:55:59 -05001470 EventLogger::getErrorType(ex),
1471 (typeid(ex) == typeid(DataException)) ||
1472 (typeid(ex) == typeid(EccException))
1473 ? types::SeverityType::Warning
1474 : types::SeverityType::Informational,
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301475 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1476 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001477
1478 // TODO: Figure out a way to clear data in case of any failure at
1479 // runtime.
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301480
1481 // set present property to false for any error case. In future this will
1482 // be replaced by presence logic.
Souvik Roy6a9553c2025-02-07 01:16:32 -06001483 // Update Present property for this FRU only if we handle Present
1484 // property for the FRU.
1485 if (isPresentPropertyHandlingRequired(
1486 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1487 {
1488 setPresentProperty(i_vpdFilePath, false);
1489 }
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301490
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001491 m_semaphore.release();
1492 return std::make_tuple(false, i_vpdFilePath);
1493 }
1494 m_semaphore.release();
1495 return std::make_tuple(true, i_vpdFilePath);
1496}
1497
Sunny Srivastava61611752025-02-04 00:29:33 -06001498bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1499{
1500 if (i_vpdFilePath.empty())
1501 {
1502 return true;
1503 }
1504
1505 // skip processing of system VPD again as it has been already collected.
1506 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1507 {
1508 return true;
1509 }
1510
1511 if (dbusUtility::isChassisPowerOn())
1512 {
1513 // If chassis is powered on, skip collecting FRUs which are
1514 // powerOffOnly.
1515 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath))
1516 {
1517 return true;
1518 }
1519
Rekha Aparna017567a2025-08-13 02:07:06 -05001520 uint16_t l_errCode = 0;
1521 std::string l_invPath = jsonUtility::getInventoryObjPathFromJson(
1522 m_parsedJson, i_vpdFilePath, l_errCode);
1523
1524 if (l_errCode)
1525 {
1526 logging::logMessage(
1527 "Failed to get inventory path from JSON for FRU [" +
1528 i_vpdFilePath +
1529 "], error : " + vpdSpecificUtility::getErrCodeMsg(l_errCode));
1530
1531 return false;
1532 }
1533
Sunny Srivastava61611752025-02-04 00:29:33 -06001534 const std::string& l_invPathLeafValue =
Rekha Aparna017567a2025-08-13 02:07:06 -05001535 sdbusplus::message::object_path(l_invPath).filename();
Sunny Srivastava61611752025-02-04 00:29:33 -06001536
1537 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1538 {
1539 return true;
1540 }
1541 }
1542
1543 return false;
1544}
1545
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001546void Worker::collectFrusFromJson()
1547{
1548 // A parsed JSON file should be present to pick FRUs EEPROM paths
1549 if (m_parsedJson.empty())
1550 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301551 throw JsonException(
1552 std::string(__FUNCTION__) +
1553 ": Config JSON is mandatory for processing of FRUs through this API.",
1554 m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001555 }
1556
1557 const nlohmann::json& listOfFrus =
1558 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1559
1560 for (const auto& itemFRUS : listOfFrus.items())
1561 {
1562 const std::string& vpdFilePath = itemFRUS.key();
1563
Sunny Srivastava61611752025-02-04 00:29:33 -06001564 if (skipPathForCollection(vpdFilePath))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001565 {
1566 continue;
1567 }
1568
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001569 try
1570 {
1571 std::thread{[vpdFilePath, this]() {
1572 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001573
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001574 m_mutex.lock();
1575 m_activeCollectionThreadCount--;
1576 m_mutex.unlock();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001577
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001578 if (!m_activeCollectionThreadCount)
1579 {
1580 m_isAllFruCollected = true;
1581 }
1582 }}.detach();
1583 }
1584 catch (const std::exception& l_ex)
1585 {
1586 // add vpdFilePath(EEPROM path) to failed list
1587 m_failedEepromPaths.push_front(vpdFilePath);
1588 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001589 }
1590}
1591
1592// ToDo: Move the API under IBM_SYSTEM
1593void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1594{
1595 try
1596 {
1597 std::string l_backupAndRestoreCfgFilePath =
1598 m_parsedJson.value("backupRestoreConfigPath", "");
1599
1600 nlohmann::json l_backupAndRestoreCfgJsonObj =
1601 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath);
1602
RekhaAparna011ef21002025-02-18 23:47:36 -06001603 if (l_backupAndRestoreCfgJsonObj.empty())
1604 {
1605 throw JsonException("JSON parsing failed",
1606 l_backupAndRestoreCfgFilePath);
1607 }
1608
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001609 // check if either of "source" or "destination" has inventory path.
1610 // this indicates that this sytem has System VPD on hardware
1611 // and other copy on D-Bus (BMC cache).
1612 if (!l_backupAndRestoreCfgJsonObj.empty() &&
1613 ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1614 l_backupAndRestoreCfgJsonObj["source"].contains(
1615 "inventoryPath")) ||
1616 (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1617 l_backupAndRestoreCfgJsonObj["destination"].contains(
1618 "inventoryPath"))))
1619 {
1620 BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1621 auto [l_srcVpdVariant,
1622 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1623
1624 // ToDo: Revisit is this check is required or not.
1625 if (auto l_srcVpdMap =
1626 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1627 l_srcVpdMap && !(*l_srcVpdMap).empty())
1628 {
1629 io_srcVpdMap = std::move(l_srcVpdVariant);
1630 }
1631 }
1632 }
1633 catch (const std::exception& l_ex)
1634 {
1635 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301636 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301637 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001638 std::string(
1639 "Exception caught while backup and restore VPD keyword's.") +
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301640 EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001641 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1642 }
1643}
1644
1645void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1646{
1647 if (i_dbusObjPath.empty())
1648 {
1649 throw std::runtime_error("Given DBus object path is empty.");
1650 }
1651
1652 const std::string& l_fruPath =
1653 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1654
1655 try
1656 {
1657 auto l_presentPropValue = dbusUtility::readDbusProperty(
1658 constants::pimServiceName, i_dbusObjPath,
1659 constants::inventoryItemInf, "Present");
1660
1661 if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1662 {
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001663 uint16_t l_errCode = 0;
Souvik Roye9120152025-07-02 08:24:38 -05001664 // check if FRU's Present property is handled by vpd-manager
1665 const auto& l_isFruPresenceHandled =
Rekha Aparnaa1187a52025-09-01 12:42:19 -05001666 jsonUtility::isFruPresenceHandled(m_parsedJson, l_fruPath,
1667 l_errCode);
1668
1669 if (l_errCode)
1670 {
1671 throw std::runtime_error(
1672 "Failed to check if FRU's presence is handled, reason: " +
1673 vpdSpecificUtility::getErrCodeMsg(l_errCode));
1674 }
Souvik Roye9120152025-07-02 08:24:38 -05001675
1676 if (!(*l_value) && l_isFruPresenceHandled)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001677 {
1678 throw std::runtime_error("Given FRU is not present");
1679 }
Souvik Roye9120152025-07-02 08:24:38 -05001680 else if (*l_value && !l_isFruPresenceHandled)
1681 {
1682 throw std::runtime_error(
1683 "Given FRU is present and its presence is not handled by vpd-manager.");
1684 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001685 else
1686 {
1687 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1688 "preAction", "deletion"))
1689 {
1690 if (!processPreAction(l_fruPath, "deletion"))
1691 {
1692 throw std::runtime_error("Pre action failed");
1693 }
1694 }
1695
1696 std::vector<std::string> l_interfaceList{
1697 constants::operationalStatusInf};
1698
1699 types::MapperGetSubTree l_subTreeMap =
1700 dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1701 l_interfaceList);
1702
1703 types::ObjectMap l_objectMap;
1704
1705 // Updates VPD specific interfaces property value under PIM for
1706 // sub FRUs.
1707 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1708 l_subTreeMap)
1709 {
1710 types::InterfaceMap l_interfaceMap;
1711 vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1712 l_interfaceMap);
1713 l_objectMap.emplace(l_objectPath,
1714 std::move(l_interfaceMap));
1715 }
1716
1717 types::InterfaceMap l_interfaceMap;
1718 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1719 l_interfaceMap);
1720
1721 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1722
1723 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1724 {
1725 throw std::runtime_error("Call to PIM failed.");
1726 }
1727
1728 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1729 "postAction", "deletion"))
1730 {
1731 if (!processPostAction(l_fruPath, "deletion"))
1732 {
1733 throw std::runtime_error("Post action failed");
1734 }
1735 }
1736 }
1737 }
1738 else
1739 {
1740 logging::logMessage(
1741 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1742 "] as unable to read present property");
1743 return;
1744 }
1745
1746 logging::logMessage(
1747 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1748 }
1749 catch (const std::exception& l_ex)
1750 {
1751 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1752 "postFailAction", "deletion"))
1753 {
1754 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
1755 "deletion"))
1756 {
1757 logging::logMessage(
1758 "Post fail action failed for: " + i_dbusObjPath);
1759 }
1760 }
1761
1762 logging::logMessage("Failed to delete VPD for FRU : " + i_dbusObjPath +
1763 " error: " + std::string(l_ex.what()));
1764 }
1765}
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301766
1767void Worker::setPresentProperty(const std::string& i_vpdPath,
1768 const bool& i_value)
1769{
1770 try
1771 {
1772 if (i_vpdPath.empty())
1773 {
1774 throw std::runtime_error(
1775 "Path is empty. Can't set present property");
1776 }
1777
1778 types::ObjectMap l_objectInterfaceMap;
1779
1780 // If the given path is EEPROM path.
1781 if (m_parsedJson["frus"].contains(i_vpdPath))
1782 {
1783 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1784 {
1785 sdbusplus::message::object_path l_fruObjectPath(
1786 l_Fru["inventoryPath"]);
1787
1788 types::PropertyMap l_propertyValueMap;
1789 l_propertyValueMap.emplace("Present", i_value);
1790
1791 types::InterfaceMap l_interfaces;
1792 vpdSpecificUtility::insertOrMerge(l_interfaces,
1793 constants::inventoryItemInf,
1794 move(l_propertyValueMap));
1795
1796 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1797 std::move(l_interfaces));
1798 }
1799 }
1800 else
1801 {
1802 // consider it as an inventory path.
1803 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1804 {
1805 throw std::runtime_error(
1806 "Invalid inventory path: " + i_vpdPath);
1807 }
1808
1809 types::PropertyMap l_propertyValueMap;
1810 l_propertyValueMap.emplace("Present", i_value);
1811
1812 types::InterfaceMap l_interfaces;
1813 vpdSpecificUtility::insertOrMerge(l_interfaces,
1814 constants::inventoryItemInf,
1815 move(l_propertyValueMap));
1816
1817 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1818 }
1819
1820 // Notify PIM
1821 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1822 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301823 throw DbusException(
1824 std::string(__FUNCTION__) +
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301825 "Call to PIM failed while setting present property for path " +
1826 i_vpdPath);
1827 }
1828 }
1829 catch (const std::exception& l_ex)
1830 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301831 EventLogger::createSyncPel(
1832 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1833 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1834 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301835 }
1836}
1837
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301838void Worker::performVpdRecollection()
1839{
1840 try
1841 {
1842 // Check if system config JSON is present
1843 if (m_parsedJson.empty())
1844 {
1845 throw std::runtime_error(
1846 "System config json object is empty, can't process recollection.");
1847 }
1848
1849 const auto& l_frusReplaceableAtStandby =
1850 jsonUtility::getListOfFrusReplaceableAtStandby(m_parsedJson);
1851
1852 for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
1853 {
1854 // ToDo: Add some logic/trace to know the flow to
1855 // collectSingleFruVpd has been directed via
1856 // performVpdRecollection.
1857 collectSingleFruVpd(l_fruInventoryPath);
1858 }
1859 return;
1860 }
1861
1862 catch (const std::exception& l_ex)
1863 {
1864 // TODO Log PEL
1865 logging::logMessage(
1866 "VPD recollection failed with error: " + std::string(l_ex.what()));
1867 }
1868}
1869
1870void Worker::collectSingleFruVpd(
1871 const sdbusplus::message::object_path& i_dbusObjPath)
1872{
Anupama B R48f297b2025-08-13 04:29:06 -05001873 std::string l_fruPath{};
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301874 try
1875 {
1876 // Check if system config JSON is present
1877 if (m_parsedJson.empty())
1878 {
1879 logging::logMessage(
1880 "System config JSON object not present. Single FRU VPD collection is not performed for " +
1881 std::string(i_dbusObjPath));
1882 return;
1883 }
1884
1885 // Get FRU path for the given D-bus object path from JSON
Anupama B R48f297b2025-08-13 04:29:06 -05001886 l_fruPath =
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301887 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1888
1889 if (l_fruPath.empty())
1890 {
1891 logging::logMessage(
1892 "D-bus object path not present in JSON. Single FRU VPD collection is not performed for " +
1893 std::string(i_dbusObjPath));
1894 return;
1895 }
1896
1897 // Check if host is up and running
1898 if (dbusUtility::isHostRunning())
1899 {
1900 if (!jsonUtility::isFruReplaceableAtRuntime(m_parsedJson,
1901 l_fruPath))
1902 {
1903 logging::logMessage(
1904 "Given FRU is not replaceable at host runtime. Single FRU VPD collection is not performed for " +
1905 std::string(i_dbusObjPath));
1906 return;
1907 }
1908 }
1909 else if (dbusUtility::isBMCReady())
1910 {
1911 if (!jsonUtility::isFruReplaceableAtStandby(m_parsedJson,
1912 l_fruPath) &&
1913 (!jsonUtility::isFruReplaceableAtRuntime(m_parsedJson,
1914 l_fruPath)))
1915 {
1916 logging::logMessage(
1917 "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection is not performed for " +
1918 std::string(i_dbusObjPath));
1919 return;
1920 }
1921 }
1922
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001923 // Set collection Status as InProgress. Since it's an intermediate state
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301924 // D-bus set-property call is good enough to update the status.
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001925 const std::string& l_collStatusProp = "Status";
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301926
1927 if (!dbusUtility::writeDbusProperty(
1928 jsonUtility::getServiceName(m_parsedJson,
1929 std::string(i_dbusObjPath)),
1930 std::string(i_dbusObjPath), constants::vpdCollectionInterface,
1931 l_collStatusProp,
1932 types::DbusVariantType{constants::vpdCollectionInProgress}))
1933 {
1934 logging::logMessage(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001935 "Unable to set collection Status as InProgress for " +
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301936 std::string(i_dbusObjPath) +
1937 ". Continue single FRU VPD collection.");
1938 }
1939
1940 // Parse VPD
1941 types::VPDMapVariant l_parsedVpd = parseVpdFile(l_fruPath);
1942
1943 // If l_parsedVpd is pointing to std::monostate
1944 if (l_parsedVpd.index() == 0)
1945 {
1946 throw std::runtime_error(
1947 "VPD parsing failed for " + std::string(i_dbusObjPath));
1948 }
1949
1950 // Get D-bus object map from worker class
1951 types::ObjectMap l_dbusObjectMap;
1952 populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
1953
1954 if (l_dbusObjectMap.empty())
1955 {
1956 throw std::runtime_error(
1957 "Failed to create D-bus object map. Single FRU VPD collection failed for " +
1958 std::string(i_dbusObjPath));
1959 }
1960
1961 // Call PIM's Notify method
1962 if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
1963 {
1964 throw std::runtime_error(
1965 "Notify PIM failed. Single FRU VPD collection failed for " +
1966 std::string(i_dbusObjPath));
1967 }
1968 }
1969 catch (const std::exception& l_error)
1970 {
Anupama B R48f297b2025-08-13 04:29:06 -05001971 setCollectionStatusProperty(l_fruPath, constants::vpdCollectionFailed);
Sunny Srivastava380efbb2025-04-25 10:28:30 +05301972 // TODO: Log PEL
1973 logging::logMessage(std::string(l_error.what()));
1974 }
1975}
Anupama B R24691d22025-05-21 08:14:15 -05001976
1977void Worker::setCollectionStatusProperty(
1978 const std::string& i_vpdPath, const std::string& i_value) const noexcept
1979{
1980 try
1981 {
1982 if (i_vpdPath.empty())
1983 {
1984 throw std::runtime_error(
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001985 "Given path is empty. Can't set collection Status property");
Anupama B R24691d22025-05-21 08:14:15 -05001986 }
1987
1988 types::ObjectMap l_objectInterfaceMap;
1989
1990 if (m_parsedJson["frus"].contains(i_vpdPath))
1991 {
1992 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1993 {
1994 sdbusplus::message::object_path l_fruObjectPath(
1995 l_Fru["inventoryPath"]);
1996
1997 types::PropertyMap l_propertyValueMap;
Anupama B R5cd1b2d2025-08-05 04:57:40 -05001998 l_propertyValueMap.emplace("Status", i_value);
Anupama B R24691d22025-05-21 08:14:15 -05001999
2000 types::InterfaceMap l_interfaces;
2001 vpdSpecificUtility::insertOrMerge(
2002 l_interfaces, constants::vpdCollectionInterface,
2003 move(l_propertyValueMap));
2004
2005 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
2006 std::move(l_interfaces));
2007 }
2008 }
2009 else
2010 {
2011 // consider it as an inventory path.
2012 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
2013 {
2014 throw std::runtime_error(
2015 "Invalid inventory path: " + i_vpdPath +
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002016 ". Can't set collection Status property");
Anupama B R24691d22025-05-21 08:14:15 -05002017 }
2018
2019 types::PropertyMap l_propertyValueMap;
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002020 l_propertyValueMap.emplace("Status", i_value);
Anupama B R24691d22025-05-21 08:14:15 -05002021
2022 types::InterfaceMap l_interfaces;
2023 vpdSpecificUtility::insertOrMerge(l_interfaces,
2024 constants::vpdCollectionInterface,
2025 move(l_propertyValueMap));
2026
2027 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
2028 }
2029
2030 // Notify PIM
2031 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
2032 {
2033 throw DbusException(
2034 std::string(__FUNCTION__) +
Anupama B R5cd1b2d2025-08-05 04:57:40 -05002035 "Call to PIM failed while setting collection Status property for path " +
Anupama B R24691d22025-05-21 08:14:15 -05002036 i_vpdPath);
2037 }
2038 }
2039 catch (const std::exception& l_ex)
2040 {
2041 EventLogger::createSyncPel(
2042 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
2043 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
2044 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
2045 }
2046}
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05002047} // namespace vpd