blob: ac104bbf9d81d2d73d8426f085108f9e32b695c5 [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
64void Worker::enableMuxChips()
65{
66 if (m_parsedJson.empty())
67 {
68 // config JSON should not be empty at this point of execution.
69 throw std::runtime_error("Config JSON is empty. Can't enable muxes");
70 return;
71 }
72
73 if (!m_parsedJson.contains("muxes"))
74 {
75 logging::logMessage("No mux defined for the system in config JSON");
76 return;
77 }
78
79 // iterate over each MUX detail and enable them.
80 for (const auto& item : m_parsedJson["muxes"])
81 {
82 if (item.contains("holdidlepath"))
83 {
84 std::string cmd = "echo 0 > ";
85 cmd += item["holdidlepath"];
86
87 logging::logMessage("Enabling mux with command = " + cmd);
88
89 commonUtility::executeCmd(cmd);
90 continue;
91 }
92
93 logging::logMessage(
94 "Mux Entry does not have hold idle path. Can't enable the mux");
95 }
96}
97
98#ifdef IBM_SYSTEM
99void Worker::primeSystemBlueprint()
100{
101 if (m_parsedJson.empty())
102 {
103 return;
104 }
105
106 const nlohmann::json& l_listOfFrus =
107 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
108
109 for (const auto& l_itemFRUS : l_listOfFrus.items())
110 {
111 const std::string& l_vpdFilePath = l_itemFRUS.key();
112
113 if (l_vpdFilePath == SYSTEM_VPD_FILE_PATH)
114 {
115 continue;
116 }
117
118 // Prime the inventry for FRUs which
119 // are not present/processing had some error.
120 if (!primeInventory(l_vpdFilePath))
121 {
122 logging::logMessage(
123 "Priming of inventory failed for FRU " + l_vpdFilePath);
124 }
125 }
126}
127
128void Worker::performInitialSetup()
129{
130 try
131 {
132 if (!dbusUtility::isChassisPowerOn())
133 {
134 logging::logMessage("Chassis is in Off state.");
135 setDeviceTreeAndJson();
136 primeSystemBlueprint();
137 }
138
139 // Enable all mux which are used for connecting to the i2c on the
140 // pcie slots for pcie cards. These are not enabled by kernel due to
141 // an issue seen with Castello cards, where the i2c line hangs on a
142 // probe.
143 enableMuxChips();
144
145 // Nothing needs to be done. Service restarted or BMC re-booted for
146 // some reason at system power on.
147 return;
148 }
Sunny Srivastava043955d2025-01-21 18:04:49 +0530149 catch (const std::exception& l_ex)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500150 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530151 // Any issue in system's inital set up is handled in this catch. Error
152 // will not propogate to manager.
153 EventLogger::createSyncPel(
154 EventLogger::getErrorType(l_ex), types::SeverityType::Critical,
155 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
156 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500157 }
158}
159#endif
160
161static std::string readFitConfigValue()
162{
163 std::vector<std::string> output =
164 commonUtility::executeCmd("/sbin/fw_printenv");
165 std::string fitConfigValue;
166
167 for (const auto& entry : output)
168 {
169 auto pos = entry.find("=");
170 auto key = entry.substr(0, pos);
171 if (key != "fitconfig")
172 {
173 continue;
174 }
175
176 if (pos + 1 < entry.size())
177 {
178 fitConfigValue = entry.substr(pos + 1);
179 }
180 }
181
182 return fitConfigValue;
183}
184
185bool Worker::isSystemVPDOnDBus() const
186{
187 const std::string& mboardPath =
188 m_parsedJson["frus"][SYSTEM_VPD_FILE_PATH].at(0).value(
189 "inventoryPath", "");
190
191 if (mboardPath.empty())
192 {
193 throw JsonException("System vpd file path missing in JSON",
194 INVENTORY_JSON_SYM_LINK);
195 }
196
197 std::array<const char*, 1> interfaces = {
198 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
199
200 const types::MapperGetObject& objectMap =
201 dbusUtility::getObjectMap(mboardPath, interfaces);
202
203 if (objectMap.empty())
204 {
205 return false;
206 }
207 return true;
208}
209
210std::string Worker::getIMValue(const types::IPZVpdMap& parsedVpd) const
211{
212 if (parsedVpd.empty())
213 {
214 throw std::runtime_error("Empty VPD map. Can't Extract IM value");
215 }
216
217 const auto& itrToVSBP = parsedVpd.find("VSBP");
218 if (itrToVSBP == parsedVpd.end())
219 {
220 throw DataException("VSBP record missing.");
221 }
222
223 const auto& itrToIM = (itrToVSBP->second).find("IM");
224 if (itrToIM == (itrToVSBP->second).end())
225 {
226 throw DataException("IM keyword missing.");
227 }
228
229 types::BinaryVector imVal;
230 std::copy(itrToIM->second.begin(), itrToIM->second.end(),
231 back_inserter(imVal));
232
233 std::ostringstream imData;
234 for (auto& aByte : imVal)
235 {
236 imData << std::setw(2) << std::setfill('0') << std::hex
237 << static_cast<int>(aByte);
238 }
239
240 return imData.str();
241}
242
243std::string Worker::getHWVersion(const types::IPZVpdMap& parsedVpd) const
244{
245 if (parsedVpd.empty())
246 {
247 throw std::runtime_error("Empty VPD map. Can't Extract HW value");
248 }
249
250 const auto& itrToVINI = parsedVpd.find("VINI");
251 if (itrToVINI == parsedVpd.end())
252 {
253 throw DataException("VINI record missing.");
254 }
255
256 const auto& itrToHW = (itrToVINI->second).find("HW");
257 if (itrToHW == (itrToVINI->second).end())
258 {
259 throw DataException("HW keyword missing.");
260 }
261
262 types::BinaryVector hwVal;
263 std::copy(itrToHW->second.begin(), itrToHW->second.end(),
264 back_inserter(hwVal));
265
266 // The planar pass only comes from the LSB of the HW keyword,
267 // where as the MSB is used for other purposes such as signifying clock
268 // termination.
269 hwVal[0] = 0x00;
270
271 std::ostringstream hwString;
272 for (auto& aByte : hwVal)
273 {
274 hwString << std::setw(2) << std::setfill('0') << std::hex
275 << static_cast<int>(aByte);
276 }
277
278 return hwString.str();
279}
280
281void Worker::fillVPDMap(const std::string& vpdFilePath,
282 types::VPDMapVariant& vpdMap)
283{
284 logging::logMessage(std::string("Parsing file = ") + vpdFilePath);
285
286 if (vpdFilePath.empty())
287 {
288 throw std::runtime_error("Invalid file path passed to fillVPDMap API.");
289 }
290
291 if (!std::filesystem::exists(vpdFilePath))
292 {
293 throw std::runtime_error("Can't Find physical file");
294 }
295
Sunny Srivastava043955d2025-01-21 18:04:49 +0530296 std::shared_ptr<Parser> vpdParser =
297 std::make_shared<Parser>(vpdFilePath, m_parsedJson);
298 vpdMap = vpdParser->parse();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500299}
300
301void Worker::getSystemJson(std::string& systemJson,
302 const types::VPDMapVariant& parsedVpdMap)
303{
304 if (auto pVal = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
305 {
306 std::string hwKWdValue = getHWVersion(*pVal);
307 if (hwKWdValue.empty())
308 {
309 throw DataException("HW value fetched is empty.");
310 }
311
312 const std::string& imKwdValue = getIMValue(*pVal);
313 if (imKwdValue.empty())
314 {
315 throw DataException("IM value fetched is empty.");
316 }
317
318 auto itrToIM = config::systemType.find(imKwdValue);
319 if (itrToIM == config::systemType.end())
320 {
321 throw DataException("IM keyword does not map to any system type");
322 }
323
324 const types::HWVerList hwVersionList = itrToIM->second.second;
325 if (!hwVersionList.empty())
326 {
327 transform(hwKWdValue.begin(), hwKWdValue.end(), hwKWdValue.begin(),
328 ::toupper);
329
330 auto itrToHW =
331 std::find_if(hwVersionList.begin(), hwVersionList.end(),
332 [&hwKWdValue](const auto& aPair) {
333 return aPair.first == hwKWdValue;
334 });
335
336 if (itrToHW != hwVersionList.end())
337 {
338 if (!(*itrToHW).second.empty())
339 {
340 systemJson += (*itrToIM).first + "_" + (*itrToHW).second +
341 ".json";
342 }
343 else
344 {
345 systemJson += (*itrToIM).first + ".json";
346 }
347 return;
348 }
349 }
350 systemJson += itrToIM->second.first + ".json";
351 return;
352 }
353
Sunny Srivastava043955d2025-01-21 18:04:49 +0530354 throw DataException(
355 "Invalid VPD type returned from Parser. Can't get system JSON.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500356}
357
358static void setEnvAndReboot(const std::string& key, const std::string& value)
359{
360 // set env and reboot and break.
361 commonUtility::executeCmd("/sbin/fw_setenv", key, value);
362 logging::logMessage("Rebooting BMC to pick up new device tree");
363
364 // make dbus call to reboot
365 auto bus = sdbusplus::bus::new_default_system();
366 auto method = bus.new_method_call(
367 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
368 "org.freedesktop.systemd1.Manager", "Reboot");
369 bus.call_noreply(method);
370}
371
372void Worker::setJsonSymbolicLink(const std::string& i_systemJson)
373{
374 std::error_code l_ec;
375 l_ec.clear();
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530376
377 // Check if symlink file path exists and if the JSON at this location is a
378 // symlink.
379 if (m_isSymlinkPresent &&
380 std::filesystem::is_symlink(INVENTORY_JSON_SYM_LINK, l_ec))
381 { // Don't care about exception in "is_symlink". Will continue with creation
382 // of symlink.
383
384 const auto& l_symlinkFilePth =
385 std::filesystem::read_symlink(INVENTORY_JSON_SYM_LINK, l_ec);
386
387 if (l_ec)
388 {
389 logging::logMessage(
390 "Can't read existing symlink. Error =" + l_ec.message() +
391 "Trying removal of symlink and creation of new symlink.");
392 }
393
394 // If currently set JSON is the required one. No further processing
395 // required.
396 if (i_systemJson == l_symlinkFilePth)
397 {
398 // Correct symlink already set.
399 return;
400 }
401
402 if (!std::filesystem::remove(INVENTORY_JSON_SYM_LINK, l_ec))
403 {
404 // No point going further. If removal fails for existing symlink,
405 // create will anyways throw.
406 throw std::runtime_error(
407 "Removal of symlink failed with Error = " + l_ec.message() +
408 ". Can't proceed with create_symlink.");
409 }
410 }
411
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500412 if (!std::filesystem::exists(VPD_SYMLIMK_PATH, l_ec))
413 {
414 if (l_ec)
415 {
416 throw std::runtime_error(
417 "File system call to exist failed with error = " +
418 l_ec.message());
419 }
420
421 // implies it is a fresh boot/factory reset.
422 // Create the directory for hosting the symlink
423 if (!std::filesystem::create_directories(VPD_SYMLIMK_PATH, l_ec))
424 {
425 if (l_ec)
426 {
427 throw std::runtime_error(
428 "File system call to create directory failed with error = " +
429 l_ec.message());
430 }
431 }
432 }
433
434 // create a new symlink based on the system
435 std::filesystem::create_symlink(i_systemJson, INVENTORY_JSON_SYM_LINK,
436 l_ec);
437
438 if (l_ec)
439 {
440 throw std::runtime_error(
441 "create_symlink system call failed with error: " + l_ec.message());
442 }
443
444 // If the flow is at this point implies the symlink was not present there.
445 // Considering this as factory reset.
446 m_isFactoryResetDone = true;
447}
448
449void Worker::setDeviceTreeAndJson()
450{
451 // JSON is madatory for processing of this API.
452 if (m_parsedJson.empty())
453 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530454 throw JsonException("System config JSON is empty", m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500455 }
456
457 types::VPDMapVariant parsedVpdMap;
458 fillVPDMap(SYSTEM_VPD_FILE_PATH, parsedVpdMap);
459
460 // Implies it is default JSON.
461 std::string systemJson{JSON_ABSOLUTE_PATH_PREFIX};
462
463 // ToDo: Need to check if INVENTORY_JSON_SYM_LINK pointing to correct system
464 // This is required to support movement from rainier to Blue Ridge on the
465 // fly.
466
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530467 getSystemJson(systemJson, parsedVpdMap);
468
469 if (!systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500470 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530471 throw DataException(
472 "No system JSON found corresponding to IM read from VPD.");
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530473 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500474
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530475 // re-parse the JSON once appropriate JSON has been selected.
476 m_parsedJson = jsonUtility::getParsedJson(systemJson);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500477
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530478 if (m_parsedJson.empty())
479 {
480 throw(JsonException("Json parsing failed", systemJson));
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500481 }
482
483 std::string devTreeFromJson;
484 if (m_parsedJson.contains("devTree"))
485 {
486 devTreeFromJson = m_parsedJson["devTree"];
487
488 if (devTreeFromJson.empty())
489 {
Sunny Srivastava043955d2025-01-21 18:04:49 +0530490 EventLogger::createSyncPel(
491 types::ErrorType::JsonFailure, types::SeverityType::Error,
492 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500493 "Mandatory value for device tree missing from JSON[" +
Sunny Srivastava043955d2025-01-21 18:04:49 +0530494 systemJson + "]",
495 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500496 }
497 }
498
499 auto fitConfigVal = readFitConfigValue();
500
501 if (devTreeFromJson.empty() ||
502 fitConfigVal.find(devTreeFromJson) != std::string::npos)
503 { // Skipping setting device tree as either devtree info is missing from
504 // Json or it is rightly set.
505
Sunny Srivastavaadff7882025-03-13 11:41:05 +0530506 setJsonSymbolicLink(systemJson);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500507
508 if (isSystemVPDOnDBus() &&
509 jsonUtility::isBackupAndRestoreRequired(m_parsedJson))
510 {
511 performBackupAndRestore(parsedVpdMap);
512 }
513
514 // proceed to publish system VPD.
515 publishSystemVPD(parsedVpdMap);
516 return;
517 }
518
519 setEnvAndReboot("fitconfig", devTreeFromJson);
520 exit(EXIT_SUCCESS);
521}
522
523void Worker::populateIPZVPDpropertyMap(
524 types::InterfaceMap& interfacePropMap,
525 const types::IPZKwdValueMap& keyordValueMap,
526 const std::string& interfaceName)
527{
528 types::PropertyMap propertyValueMap;
529 for (const auto& kwdVal : keyordValueMap)
530 {
531 auto kwd = kwdVal.first;
532
533 if (kwd[0] == '#')
534 {
535 kwd = std::string("PD_") + kwd[1];
536 }
537 else if (isdigit(kwd[0]))
538 {
539 kwd = std::string("N_") + kwd;
540 }
541
542 types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end());
543 propertyValueMap.emplace(move(kwd), move(value));
544 }
545
546 if (!propertyValueMap.empty())
547 {
548 interfacePropMap.emplace(interfaceName, propertyValueMap);
549 }
550}
551
552void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
553 types::InterfaceMap& interfaceMap)
554{
555 for (const auto& kwdValMap : keyordVPDMap)
556 {
557 types::PropertyMap propertyValueMap;
558 auto kwd = kwdValMap.first;
559
560 if (kwd[0] == '#')
561 {
562 kwd = std::string("PD_") + kwd[1];
563 }
564 else if (isdigit(kwd[0]))
565 {
566 kwd = std::string("N_") + kwd;
567 }
568
569 if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second))
570 {
571 types::BinaryVector value((*keywordValue).begin(),
572 (*keywordValue).end());
573 propertyValueMap.emplace(move(kwd), move(value));
574 }
575 else if (auto keywordValue = get_if<std::string>(&kwdValMap.second))
576 {
577 types::BinaryVector value((*keywordValue).begin(),
578 (*keywordValue).end());
579 propertyValueMap.emplace(move(kwd), move(value));
580 }
581 else if (auto keywordValue = get_if<size_t>(&kwdValMap.second))
582 {
583 if (kwd == "MemorySizeInKB")
584 {
585 types::PropertyMap memProp;
586 memProp.emplace(move(kwd), ((*keywordValue)));
587 interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm",
588 move(memProp));
589 continue;
590 }
591 else
592 {
593 logging::logMessage(
594 "Unknown Keyword =" + kwd + " found in keyword VPD map");
595 continue;
596 }
597 }
598 else
599 {
600 logging::logMessage(
601 "Unknown variant type found in keyword VPD map.");
602 continue;
603 }
604
605 if (!propertyValueMap.empty())
606 {
607 vpdSpecificUtility::insertOrMerge(
608 interfaceMap, constants::kwdVpdInf, move(propertyValueMap));
609 }
610 }
611}
612
613void Worker::populateInterfaces(const nlohmann::json& interfaceJson,
614 types::InterfaceMap& interfaceMap,
615 const types::VPDMapVariant& parsedVpdMap)
616{
617 for (const auto& interfacesPropPair : interfaceJson.items())
618 {
619 const std::string& interface = interfacesPropPair.key();
620 types::PropertyMap propertyMap;
621
622 for (const auto& propValuePair : interfacesPropPair.value().items())
623 {
624 const std::string property = propValuePair.key();
625
626 if (propValuePair.value().is_boolean())
627 {
628 propertyMap.emplace(property,
629 propValuePair.value().get<bool>());
630 }
631 else if (propValuePair.value().is_string())
632 {
633 if (property.compare("LocationCode") == 0 &&
634 interface.compare("com.ibm.ipzvpd.Location") == 0)
635 {
636 std::string value =
637 vpdSpecificUtility::getExpandedLocationCode(
638 propValuePair.value().get<std::string>(),
639 parsedVpdMap);
640 propertyMap.emplace(property, value);
641
642 auto l_locCodeProperty = propertyMap;
643 vpdSpecificUtility::insertOrMerge(
644 interfaceMap,
645 std::string(constants::xyzLocationCodeInf),
646 move(l_locCodeProperty));
647 }
648 else
649 {
650 propertyMap.emplace(
651 property, propValuePair.value().get<std::string>());
652 }
653 }
654 else if (propValuePair.value().is_array())
655 {
656 try
657 {
658 propertyMap.emplace(
659 property,
660 propValuePair.value().get<types::BinaryVector>());
661 }
662 catch (const nlohmann::detail::type_error& e)
663 {
664 std::cerr << "Type exception: " << e.what() << "\n";
665 }
666 }
667 else if (propValuePair.value().is_number())
668 {
669 // For now assume the value is a size_t. In the future it would
670 // be nice to come up with a way to get the type from the JSON.
671 propertyMap.emplace(property,
672 propValuePair.value().get<size_t>());
673 }
674 else if (propValuePair.value().is_object())
675 {
676 const std::string& record =
677 propValuePair.value().value("recordName", "");
678 const std::string& keyword =
679 propValuePair.value().value("keywordName", "");
680 const std::string& encoding =
681 propValuePair.value().value("encoding", "");
682
683 if (auto ipzVpdMap =
684 std::get_if<types::IPZVpdMap>(&parsedVpdMap))
685 {
686 if (!record.empty() && !keyword.empty() &&
687 (*ipzVpdMap).count(record) &&
688 (*ipzVpdMap).at(record).count(keyword))
689 {
690 auto encoded = vpdSpecificUtility::encodeKeyword(
691 ((*ipzVpdMap).at(record).at(keyword)), encoding);
692 propertyMap.emplace(property, encoded);
693 }
694 }
695 else if (auto kwdVpdMap =
696 std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
697 {
698 if (!keyword.empty() && (*kwdVpdMap).count(keyword))
699 {
700 if (auto kwValue = std::get_if<types::BinaryVector>(
701 &(*kwdVpdMap).at(keyword)))
702 {
703 auto encodedValue =
704 vpdSpecificUtility::encodeKeyword(
705 std::string((*kwValue).begin(),
706 (*kwValue).end()),
707 encoding);
708
709 propertyMap.emplace(property, encodedValue);
710 }
711 else if (auto kwValue = std::get_if<std::string>(
712 &(*kwdVpdMap).at(keyword)))
713 {
714 auto encodedValue =
715 vpdSpecificUtility::encodeKeyword(
716 std::string((*kwValue).begin(),
717 (*kwValue).end()),
718 encoding);
719
720 propertyMap.emplace(property, encodedValue);
721 }
722 else if (auto uintValue = std::get_if<size_t>(
723 &(*kwdVpdMap).at(keyword)))
724 {
725 propertyMap.emplace(property, *uintValue);
726 }
727 else
728 {
729 logging::logMessage(
730 "Unknown keyword found, Keywrod = " + keyword);
731 }
732 }
733 }
734 }
735 }
736 vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
737 move(propertyMap));
738 }
739}
740
741bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
742{
743 const unsigned char l_io[] = {
744 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
745 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
746
747 // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
748 // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
749 // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
750 // IO.
751 if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
752 constants::SIZE_OF_8EQ_IN_PG) == 0)
753 {
754 return true;
755 }
756
757 // The CPU is not an IO
758 return false;
759}
760
761bool Worker::primeInventory(const std::string& i_vpdFilePath)
762{
763 if (i_vpdFilePath.empty())
764 {
765 logging::logMessage("Empty VPD file path given");
766 return false;
767 }
768
769 if (m_parsedJson.empty())
770 {
771 logging::logMessage("Empty JSON detected for " + i_vpdFilePath);
772 return false;
773 }
774 else if (!m_parsedJson["frus"].contains(i_vpdFilePath))
775 {
776 logging::logMessage("File " + i_vpdFilePath +
777 ", is not found in the system config JSON file.");
778 return false;
779 }
780
781 types::ObjectMap l_objectInterfaceMap;
782 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdFilePath])
783 {
784 types::InterfaceMap l_interfaces;
785 sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
786
787 if (l_Fru.contains("ccin"))
788 {
789 continue;
790 }
791
792 if (l_Fru.contains("noprime") && l_Fru.value("noprime", false))
793 {
794 continue;
795 }
796
Souvik Roy6a9553c2025-02-07 01:16:32 -0600797 // Reset data under PIM for this FRU only if the FRU is not synthesized
798 // and we handle it's Present property.
799 if (isPresentPropertyHandlingRequired(l_Fru))
800 {
801 // Clear data under PIM if already exists.
802 vpdSpecificUtility::resetDataUnderPIM(
803 std::string(l_Fru["inventoryPath"]), l_interfaces);
804 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500805
806 // Add extra interfaces mentioned in the Json config file
807 if (l_Fru.contains("extraInterfaces"))
808 {
809 populateInterfaces(l_Fru["extraInterfaces"], l_interfaces,
810 std::monostate{});
811 }
812
813 types::PropertyMap l_propertyValueMap;
Sunny Srivastavad159bb42025-01-09 11:13:50 +0530814
Souvik Roy6a9553c2025-02-07 01:16:32 -0600815 // Update Present property for this FRU only if we handle Present
816 // property for the FRU.
817 if (isPresentPropertyHandlingRequired(l_Fru))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500818 {
Souvik Roy6a9553c2025-02-07 01:16:32 -0600819 l_propertyValueMap.emplace("Present", false);
820
821 // TODO: Present based on file will be taken care in future.
822 // By default present is set to false for FRU at the time of
823 // priming. Once collection goes through, it will be set to true in
824 // that flow.
825 /*if (std::filesystem::exists(i_vpdFilePath))
826 {
827 l_propertyValueMap["Present"] = true;
828 }*/
829 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500830
831 vpdSpecificUtility::insertOrMerge(l_interfaces,
832 "xyz.openbmc_project.Inventory.Item",
833 move(l_propertyValueMap));
834
835 if (l_Fru.value("inherit", true) &&
836 m_parsedJson.contains("commonInterfaces"))
837 {
838 populateInterfaces(m_parsedJson["commonInterfaces"], l_interfaces,
839 std::monostate{});
840 }
841
842 processFunctionalProperty(l_Fru["inventoryPath"], l_interfaces);
843 processEnabledProperty(l_Fru["inventoryPath"], l_interfaces);
844
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -0600845 // Emplace the default state of FRU VPD collection
846 types::PropertyMap l_fruCollectionProperty = {
847 {"CollectionStatus", constants::vpdCollectionNotStarted}};
848
849 vpdSpecificUtility::insertOrMerge(l_interfaces,
850 constants::vpdCollectionInterface,
851 std::move(l_fruCollectionProperty));
852
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500853 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
854 std::move(l_interfaces));
855 }
856
857 // Notify PIM
858 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
859 {
860 logging::logMessage("Call to PIM failed for VPD file " + i_vpdFilePath);
861 return false;
862 }
863
864 return true;
865}
866
867void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
868 types::InterfaceMap& interfaces)
869{
870 // embedded property(true or false) says whether the subfru is embedded
871 // into the parent fru (or) not. VPD sets Present property only for
872 // embedded frus. If the subfru is not an embedded FRU, the subfru may
873 // or may not be physically present. Those non embedded frus will always
874 // have Present=false irrespective of its physical presence or absence.
875 // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
876 // Present to true for such sub frus.
877 // Eg: ethernet port is embedded into bmc card. So set Present to true
878 // for such sub frus. Also donot populate present property for embedded
879 // subfru which is synthesized. Currently there is no subfru which are
880 // both embedded and synthesized. But still the case is handled here.
881
882 // Check if its required to handle presence for this FRU.
883 if (singleFru.value("handlePresence", true))
884 {
885 types::PropertyMap presProp;
886 presProp.emplace("Present", true);
887 vpdSpecificUtility::insertOrMerge(
888 interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp));
889 }
890}
891
892void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
893 types::InterfaceMap& interfaces,
894 const types::VPDMapVariant& parsedVpdMap)
895{
896 populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
897 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
898 {
899 if (singleFru["extraInterfaces"].contains(
900 "xyz.openbmc_project.Inventory.Item.Cpu"))
901 {
902 auto itrToRec = (*ipzVpdMap).find("CP00");
903 if (itrToRec == (*ipzVpdMap).end())
904 {
905 return;
906 }
907
Souvik Roya55fcca2025-02-19 01:33:58 -0600908 const std::string pgKeywordValue{
909 vpdSpecificUtility::getKwVal(itrToRec->second, "PG")};
910
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500911 if (!pgKeywordValue.empty())
912 {
913 if (isCPUIOGoodOnly(pgKeywordValue))
914 {
915 interfaces["xyz.openbmc_project.Inventory.Item"]
916 ["PrettyName"] = "IO Module";
917 }
918 }
Souvik Roya55fcca2025-02-19 01:33:58 -0600919 else
920 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +0530921 throw DataException(std::string(__FUNCTION__) +
922 "Failed to get value for keyword PG");
Souvik Roya55fcca2025-02-19 01:33:58 -0600923 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500924 }
925 }
926}
927
928void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
929 const types::VPDMapVariant& parsedVpdMap,
930 types::InterfaceMap& interfaces)
931{
932 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
933 {
934 for (const auto& record : singleFru["copyRecords"])
935 {
936 const std::string& recordName = record;
937 if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
938 {
939 populateIPZVPDpropertyMap(interfaces,
940 (*ipzVpdMap).at(recordName),
941 constants::ipzVpdInf + recordName);
942 }
943 }
944 }
945}
946
947void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
948 types::InterfaceMap& interfaces)
949{
950 if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
951 {
952 for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
953 {
954 populateIPZVPDpropertyMap(interfaces, kwdValueMap,
955 constants::ipzVpdInf + recordName);
956 }
957 }
958 else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
959 {
960 populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
961 }
962
963 if (m_parsedJson.contains("commonInterfaces"))
964 {
965 populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
966 parsedVpdMap);
967 }
968}
969
970bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
971 const types::VPDMapVariant& parsedVpdMap)
972{
973 if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
974 {
975 auto itrToRec = (*ipzVPDMap).find("VINI");
976 if (itrToRec == (*ipzVPDMap).end())
977 {
978 return false;
979 }
980
Souvik Roya55fcca2025-02-19 01:33:58 -0600981 std::string ccinFromVpd{
982 vpdSpecificUtility::getKwVal(itrToRec->second, "CC")};
983
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500984 if (ccinFromVpd.empty())
985 {
986 return false;
987 }
988
989 transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
990 ::toupper);
991
992 std::vector<std::string> ccinList;
993 for (std::string ccin : singleFru["ccin"])
994 {
995 transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
996 ccinList.push_back(ccin);
997 }
998
999 if (ccinList.empty())
1000 {
1001 return false;
1002 }
1003
1004 if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
1005 ccinList.end())
1006 {
1007 return false;
1008 }
1009 }
1010 return true;
1011}
1012
1013void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
1014 types::InterfaceMap& io_interfaces)
1015{
1016 if (!dbusUtility::isChassisPowerOn())
1017 {
1018 std::array<const char*, 1> l_operationalStatusInf = {
1019 constants::operationalStatusInf};
1020
1021 auto mapperObjectMap = dbusUtility::getObjectMap(
1022 i_inventoryObjPath, l_operationalStatusInf);
1023
1024 // If the object has been found. Check if it is under PIM.
1025 if (mapperObjectMap.size() != 0)
1026 {
1027 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
1028 {
1029 if (l_serviceName == constants::pimServiceName)
1030 {
1031 // The object is already under PIM. No need to process
1032 // again. Retain the old value.
1033 return;
1034 }
1035 }
1036 }
1037
1038 // Implies value is not there in D-Bus. Populate it with default
1039 // value "true".
1040 types::PropertyMap l_functionalProp;
1041 l_functionalProp.emplace("Functional", true);
1042 vpdSpecificUtility::insertOrMerge(io_interfaces,
1043 constants::operationalStatusInf,
1044 move(l_functionalProp));
1045 }
1046
1047 // if chassis is power on. Functional property should be there on D-Bus.
1048 // Don't process.
1049 return;
1050}
1051
1052void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
1053 types::InterfaceMap& io_interfaces)
1054{
1055 if (!dbusUtility::isChassisPowerOn())
1056 {
1057 std::array<const char*, 1> l_enableInf = {constants::enableInf};
1058
1059 auto mapperObjectMap =
1060 dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
1061
1062 // If the object has been found. Check if it is under PIM.
1063 if (mapperObjectMap.size() != 0)
1064 {
1065 for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
1066 {
1067 if (l_serviceName == constants::pimServiceName)
1068 {
1069 // The object is already under PIM. No need to process
1070 // again. Retain the old value.
1071 return;
1072 }
1073 }
1074 }
1075
1076 // Implies value is not there in D-Bus. Populate it with default
1077 // value "true".
1078 types::PropertyMap l_enabledProp;
1079 l_enabledProp.emplace("Enabled", true);
1080 vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
1081 move(l_enabledProp));
1082 }
1083
1084 // if chassis is power on. Enabled property should be there on D-Bus.
1085 // Don't process.
1086 return;
1087}
1088
1089void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
1090 types::ObjectMap& objectInterfaceMap,
1091 const std::string& vpdFilePath)
1092{
1093 if (vpdFilePath.empty())
1094 {
1095 throw std::runtime_error(
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301096 std::string(__FUNCTION__) +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001097 "Invalid parameter passed to populateDbus API.");
1098 }
1099
1100 // JSON config is mandatory for processing of "if". Add "else" for any
1101 // processing without config JSON.
1102 if (!m_parsedJson.empty())
1103 {
1104 types::InterfaceMap interfaces;
1105
1106 for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
1107 {
1108 const auto& inventoryPath = aFru["inventoryPath"];
1109 sdbusplus::message::object_path fruObjectPath(inventoryPath);
1110 if (aFru.contains("ccin"))
1111 {
1112 if (!processFruWithCCIN(aFru, parsedVpdMap))
1113 {
1114 continue;
1115 }
1116 }
1117
1118 if (aFru.value("inherit", true))
1119 {
1120 processInheritFlag(parsedVpdMap, interfaces);
1121 }
1122
1123 // If specific record needs to be copied.
1124 if (aFru.contains("copyRecords"))
1125 {
1126 processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
1127 }
1128
1129 if (aFru.contains("extraInterfaces"))
1130 {
1131 // Process extra interfaces w.r.t a FRU.
1132 processExtraInterfaces(aFru, interfaces, parsedVpdMap);
1133 }
1134
1135 // Process FRUS which are embedded in the parent FRU and whose VPD
1136 // will be synthesized.
1137 if ((aFru.value("embedded", true)) &&
1138 (!aFru.value("synthesized", false)))
1139 {
1140 processEmbeddedAndSynthesizedFrus(aFru, interfaces);
1141 }
1142
1143 processFunctionalProperty(inventoryPath, interfaces);
1144 processEnabledProperty(inventoryPath, interfaces);
1145
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001146 // Update collection status as successful
1147 types::PropertyMap l_collectionProperty = {
1148 {"CollectionStatus", constants::vpdCollectionSuccess}};
1149
1150 vpdSpecificUtility::insertOrMerge(interfaces,
1151 constants::vpdCollectionInterface,
1152 std::move(l_collectionProperty));
1153
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001154 objectInterfaceMap.emplace(std::move(fruObjectPath),
1155 std::move(interfaces));
1156 }
1157 }
1158}
1159
Patrick Williams43fedab2025-02-03 14:28:05 -05001160std::string Worker::createAssetTagString(
1161 const types::VPDMapVariant& i_parsedVpdMap)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001162{
1163 std::string l_assetTag;
1164
1165 // system VPD will be in IPZ format.
1166 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
1167 {
1168 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
1169 if (l_itrToVsys != (*l_parsedVpdMap).end())
1170 {
Souvik Roya55fcca2025-02-19 01:33:58 -06001171 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal(
1172 l_itrToVsys->second, constants::kwdTM)};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001173
Souvik Roya55fcca2025-02-19 01:33:58 -06001174 if (l_tmKwdValue.empty())
1175 {
1176 throw std::runtime_error(
1177 std::string("Failed to get value for keyword [") +
1178 constants::kwdTM +
1179 std::string("] while creating Asset tag."));
1180 }
1181
1182 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal(
1183 l_itrToVsys->second, constants::kwdSE)};
1184
1185 if (l_seKwdValue.empty())
1186 {
1187 throw std::runtime_error(
1188 std::string("Failed to get value for keyword [") +
1189 constants::kwdSE +
1190 std::string("] while creating Asset tag."));
1191 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001192
1193 l_assetTag = std::string{"Server-"} + l_tmKwdValue +
1194 std::string{"-"} + l_seKwdValue;
1195 }
1196 else
1197 {
1198 throw std::runtime_error(
1199 "VSYS record not found in parsed VPD map to create Asset tag.");
1200 }
1201 }
1202 else
1203 {
1204 throw std::runtime_error(
1205 "Invalid VPD type recieved to create Asset tag.");
1206 }
1207
1208 return l_assetTag;
1209}
1210
1211void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
1212{
1213 types::ObjectMap objectInterfaceMap;
1214
1215 if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
1216 {
1217 populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
1218
1219 try
1220 {
1221 if (m_isFactoryResetDone)
1222 {
1223 const auto& l_assetTag = createAssetTagString(parsedVpdMap);
1224
1225 auto l_itrToSystemPath = objectInterfaceMap.find(
1226 sdbusplus::message::object_path(constants::systemInvPath));
1227 if (l_itrToSystemPath == objectInterfaceMap.end())
1228 {
1229 throw std::runtime_error(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301230 "Asset tag update failed. System Path not found in object map.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001231 }
1232
1233 types::PropertyMap l_assetTagProperty;
1234 l_assetTagProperty.emplace("AssetTag", l_assetTag);
1235
1236 (l_itrToSystemPath->second)
1237 .emplace(constants::assetTagInf,
1238 std::move(l_assetTagProperty));
1239 }
1240 }
1241 catch (const std::exception& l_ex)
1242 {
1243 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301244 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1245 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001246 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1247 }
1248
1249 // Notify PIM
1250 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1251 {
1252 throw std::runtime_error("Call to PIM failed for system VPD");
1253 }
1254 }
1255 else
1256 {
1257 throw DataException("Invalid format of parsed VPD map.");
1258 }
1259}
1260
1261bool Worker::processPreAction(const std::string& i_vpdFilePath,
1262 const std::string& i_flagToProcess)
1263{
1264 if (i_vpdFilePath.empty() || i_flagToProcess.empty())
1265 {
1266 logging::logMessage(
1267 "Invalid input parameter. Abort processing pre action");
1268 return false;
1269 }
1270
1271 if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
1272 i_vpdFilePath, i_flagToProcess)) &&
1273 (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
1274 {
1275 // TODO: Need a way to delete inventory object from Dbus and persisted
1276 // data section in case any FRU is not present or there is any
1277 // problem in collecting it. Once it has been deleted, it can be
1278 // re-created in the flow of priming the inventory. This needs to be
1279 // done either here or in the exception section of "parseAndPublishVPD"
1280 // API. Any failure in the process of collecting FRU will land up in the
1281 // excpetion of "parseAndPublishVPD".
1282
1283 // If the FRU is not there, clear the VINI/CCIN data.
1284 // Enity manager probes for this keyword to look for this
1285 // FRU, now if the data is persistent on BMC and FRU is
1286 // removed this can lead to ambiguity. Hence clearing this
1287 // Keyword if FRU is absent.
1288 const auto& inventoryPath =
1289 m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
1290 "");
1291
1292 if (!inventoryPath.empty())
1293 {
1294 types::ObjectMap l_pimObjMap{
1295 {inventoryPath,
1296 {{constants::kwdVpdInf,
1297 {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
1298
1299 if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
1300 {
1301 logging::logMessage(
1302 "Call to PIM failed for file " + i_vpdFilePath);
1303 }
1304 }
1305 else
1306 {
1307 logging::logMessage(
1308 "Inventory path is empty in Json for file " + i_vpdFilePath);
1309 }
1310
1311 return false;
1312 }
1313 return true;
1314}
1315
1316bool Worker::processPostAction(
1317 const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
1318 const std::optional<types::VPDMapVariant> i_parsedVpd)
1319{
1320 if (i_vpdFruPath.empty() || i_flagToProcess.empty())
1321 {
1322 logging::logMessage(
1323 "Invalid input parameter. Abort processing post action");
1324 return false;
1325 }
1326
1327 // Check if post action tag is to be triggered in the flow of collection
1328 // based on some CCIN value?
1329 if (m_parsedJson["frus"][i_vpdFruPath]
1330 .at(0)["postAction"][i_flagToProcess]
1331 .contains("ccin"))
1332 {
1333 if (!i_parsedVpd.has_value())
1334 {
1335 logging::logMessage("Empty VPD Map");
1336 return false;
1337 }
1338
1339 // CCIN match is required to process post action for this FRU as it
1340 // contains the flag.
1341 if (!vpdSpecificUtility::findCcinInVpd(
1342 m_parsedJson["frus"][i_vpdFruPath].at(
1343 0)["postAction"]["collection"],
1344 i_parsedVpd.value()))
1345 {
1346 // If CCIN is not found, implies post action processing is not
1347 // required for this FRU. Let the flow continue.
1348 return true;
1349 }
1350 }
1351
1352 if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
1353 i_vpdFruPath, i_flagToProcess))
1354 {
1355 logging::logMessage(
1356 "Execution of post action failed for path: " + i_vpdFruPath);
1357
1358 // If post action was required and failed only in that case return
1359 // false. In all other case post action is considered passed.
1360 return false;
1361 }
1362
1363 return true;
1364}
1365
1366types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
1367{
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001368 try
1369 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301370 if (i_vpdFilePath.empty())
1371 {
1372 throw std::runtime_error(
1373 std::string(__FUNCTION__) +
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301374 " Empty VPD file path passed. Abort processing");
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301375 }
1376
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301377 bool isPreActionRequired = false;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001378 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1379 "preAction", "collection"))
1380 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301381 isPreActionRequired = true;
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001382 if (!processPreAction(i_vpdFilePath, "collection"))
1383 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301384 throw std::runtime_error(
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301385 std::string(__FUNCTION__) + " Pre-Action failed");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001386 }
1387 }
1388
1389 if (!std::filesystem::exists(i_vpdFilePath))
1390 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301391 if (isPreActionRequired)
1392 {
1393 throw std::runtime_error(
1394 std::string(__FUNCTION__) + " Could not find file path " +
1395 i_vpdFilePath + "Skipping parser trigger for the EEPROM");
1396 }
1397 return types::VPDMapVariant{};
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001398 }
1399
1400 std::shared_ptr<Parser> vpdParser =
1401 std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
1402
1403 types::VPDMapVariant l_parsedVpd = vpdParser->parse();
1404
1405 // Before returning, as collection is over, check if FRU qualifies for
1406 // any post action in the flow of collection.
1407 // Note: Don't change the order, post action needs to be processed only
1408 // after collection for FRU is successfully done.
1409 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
1410 "postAction", "collection"))
1411 {
1412 if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
1413 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301414 // Post action was required but failed while executing.
1415 // Behaviour can be undefined.
1416 EventLogger::createSyncPel(
1417 types::ErrorType::InternalFailure,
1418 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
1419 std::string("Required post action failed for path [" +
1420 i_vpdFilePath + "]"),
1421 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001422 }
1423 }
1424
1425 return l_parsedVpd;
1426 }
1427 catch (std::exception& l_ex)
1428 {
1429 // If post fail action is required, execute it.
1430 if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
Sunny Srivastava4c164382025-01-28 03:17:33 -06001431 "postFailAction", "collection"))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001432 {
1433 if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
1434 "collection"))
1435 {
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001436 throw std::runtime_error(
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301437 std::string(__FUNCTION__) + "VPD parsing failed for " +
1438 i_vpdFilePath + " due to error: " + l_ex.what() +
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001439 ". Post Fail Action also failed, aborting collection for this FRU");
1440 }
1441 }
1442
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301443 throw std::runtime_error(
1444 std::string(__FUNCTION__) + "VPD parsing failed for " +
1445 i_vpdFilePath + " due to error: " + l_ex.what());
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001446 }
1447}
1448
Patrick Williams43fedab2025-02-03 14:28:05 -05001449std::tuple<bool, std::string> Worker::parseAndPublishVPD(
1450 const std::string& i_vpdFilePath)
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001451{
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001452 std::string l_inventoryPath{};
1453
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001454 try
1455 {
1456 m_semaphore.acquire();
1457
1458 // Thread launched.
1459 m_mutex.lock();
1460 m_activeCollectionThreadCount++;
1461 m_mutex.unlock();
1462
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001463 // Set CollectionStatus as InProgress. Since it's an intermediate state
1464 // D-bus set-property call is good enough to update the status.
RekhaAparna011ef21002025-02-18 23:47:36 -06001465 l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
1466 m_parsedJson, i_vpdFilePath);
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001467
RekhaAparna011ef21002025-02-18 23:47:36 -06001468 if (!l_inventoryPath.empty())
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001469 {
RekhaAparna01c532b182025-02-19 19:56:49 -06001470 if (!dbusUtility::writeDbusProperty(
RekhaAparna011ef21002025-02-18 23:47:36 -06001471 jsonUtility::getServiceName(m_parsedJson, l_inventoryPath),
1472 l_inventoryPath, constants::vpdCollectionInterface,
1473 "CollectionStatus",
RekhaAparna01c532b182025-02-19 19:56:49 -06001474 types::DbusVariantType{constants::vpdCollectionInProgress}))
RekhaAparna011ef21002025-02-18 23:47:36 -06001475 {
1476 logging::logMessage(
1477 "Unable to set CollectionStatus as InProgress for " +
RekhaAparna01c532b182025-02-19 19:56:49 -06001478 i_vpdFilePath + ". Error : " + "DBus write failed");
RekhaAparna011ef21002025-02-18 23:47:36 -06001479 }
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001480 }
1481
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001482 const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301483 if (!std::holds_alternative<std::monostate>(parsedVpdMap))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001484 {
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301485 types::ObjectMap objectInterfaceMap;
1486 populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
1487
1488 // Notify PIM
1489 if (!dbusUtility::callPIM(move(objectInterfaceMap)))
1490 {
1491 throw std::runtime_error(
1492 std::string(__FUNCTION__) +
1493 "Call to PIM failed while publishing VPD.");
1494 }
1495 }
1496 else
1497 {
1498 logging::logMessage("Empty parsedVpdMap recieved for path [" +
1499 i_vpdFilePath + "]. Check PEL for reason.");
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001500 }
1501 }
1502 catch (const std::exception& ex)
1503 {
Priyanga Ramasamy1aad7832024-12-12 22:13:52 -06001504 // Notify FRU's VPD CollectionStatus as Failure
1505 if (!dbusUtility::notifyFRUCollectionStatus(
1506 l_inventoryPath, constants::vpdCollectionFailure))
1507 {
1508 logging::logMessage(
1509 "Call to PIM Notify method failed to update Collection status as Failure for " +
1510 i_vpdFilePath);
1511 }
1512
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001513 // handle all the exceptions internally. Return only true/false
1514 // based on status of execution.
1515 if (typeid(ex) == std::type_index(typeid(DataException)))
1516 {
Sunny Srivastava78c91072025-02-05 14:09:50 +05301517 // In case of pass1 planar, VPD can be corrupted on PCIe cards. Skip
1518 // logging error for these cases.
1519 if (vpdSpecificUtility::isPass1Planar())
1520 {
RekhaAparna011ef21002025-02-18 23:47:36 -06001521 const std::string& l_invPathLeafValue =
1522 sdbusplus::message::object_path(
1523 jsonUtility::getInventoryObjPathFromJson(m_parsedJson,
1524 i_vpdFilePath))
1525 .filename();
Sunny Srivastava78c91072025-02-05 14:09:50 +05301526
RekhaAparna011ef21002025-02-18 23:47:36 -06001527 if ((l_invPathLeafValue.find("pcie_card", 0) !=
1528 std::string::npos))
Sunny Srivastava78c91072025-02-05 14:09:50 +05301529 {
1530 // skip logging any PEL for PCIe cards on pass 1 planar.
1531 return std::make_tuple(false, i_vpdFilePath);
1532 }
1533 }
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301534 }
Sunny Srivastava78c91072025-02-05 14:09:50 +05301535
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301536 EventLogger::createSyncPel(
Sunny Srivastava0a5fce12025-04-09 11:09:51 +05301537 EventLogger::getErrorType(ex), types::SeverityType::Informational,
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301538 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(ex),
1539 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001540
1541 // TODO: Figure out a way to clear data in case of any failure at
1542 // runtime.
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301543
1544 // set present property to false for any error case. In future this will
1545 // be replaced by presence logic.
Souvik Roy6a9553c2025-02-07 01:16:32 -06001546 // Update Present property for this FRU only if we handle Present
1547 // property for the FRU.
1548 if (isPresentPropertyHandlingRequired(
1549 m_parsedJson["frus"][i_vpdFilePath].at(0)))
1550 {
1551 setPresentProperty(i_vpdFilePath, false);
1552 }
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301553
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001554 m_semaphore.release();
1555 return std::make_tuple(false, i_vpdFilePath);
1556 }
1557 m_semaphore.release();
1558 return std::make_tuple(true, i_vpdFilePath);
1559}
1560
Sunny Srivastava61611752025-02-04 00:29:33 -06001561bool Worker::skipPathForCollection(const std::string& i_vpdFilePath)
1562{
1563 if (i_vpdFilePath.empty())
1564 {
1565 return true;
1566 }
1567
1568 // skip processing of system VPD again as it has been already collected.
1569 if (i_vpdFilePath == SYSTEM_VPD_FILE_PATH)
1570 {
1571 return true;
1572 }
1573
1574 if (dbusUtility::isChassisPowerOn())
1575 {
1576 // If chassis is powered on, skip collecting FRUs which are
1577 // powerOffOnly.
1578 if (jsonUtility::isFruPowerOffOnly(m_parsedJson, i_vpdFilePath))
1579 {
1580 return true;
1581 }
1582
1583 const std::string& l_invPathLeafValue =
1584 sdbusplus::message::object_path(
1585 jsonUtility::getInventoryObjPathFromJson(m_parsedJson,
1586 i_vpdFilePath))
1587 .filename();
1588
1589 if ((l_invPathLeafValue.find("pcie_card", 0) != std::string::npos))
1590 {
1591 return true;
1592 }
1593 }
1594
1595 return false;
1596}
1597
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001598void Worker::collectFrusFromJson()
1599{
1600 // A parsed JSON file should be present to pick FRUs EEPROM paths
1601 if (m_parsedJson.empty())
1602 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301603 throw JsonException(
1604 std::string(__FUNCTION__) +
1605 ": Config JSON is mandatory for processing of FRUs through this API.",
1606 m_configJsonPath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001607 }
1608
1609 const nlohmann::json& listOfFrus =
1610 m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
1611
1612 for (const auto& itemFRUS : listOfFrus.items())
1613 {
1614 const std::string& vpdFilePath = itemFRUS.key();
1615
Sunny Srivastava61611752025-02-04 00:29:33 -06001616 if (skipPathForCollection(vpdFilePath))
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001617 {
1618 continue;
1619 }
1620
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001621 try
1622 {
1623 std::thread{[vpdFilePath, this]() {
1624 const auto& l_parseResult = parseAndPublishVPD(vpdFilePath);
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001625
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001626 m_mutex.lock();
1627 m_activeCollectionThreadCount--;
1628 m_mutex.unlock();
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001629
Souvik Roy1f4c8f82025-01-23 00:37:43 -06001630 if (!m_activeCollectionThreadCount)
1631 {
1632 m_isAllFruCollected = true;
1633 }
1634 }}.detach();
1635 }
1636 catch (const std::exception& l_ex)
1637 {
1638 // add vpdFilePath(EEPROM path) to failed list
1639 m_failedEepromPaths.push_front(vpdFilePath);
1640 }
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001641 }
1642}
1643
1644// ToDo: Move the API under IBM_SYSTEM
1645void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
1646{
1647 try
1648 {
1649 std::string l_backupAndRestoreCfgFilePath =
1650 m_parsedJson.value("backupRestoreConfigPath", "");
1651
1652 nlohmann::json l_backupAndRestoreCfgJsonObj =
1653 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath);
1654
RekhaAparna011ef21002025-02-18 23:47:36 -06001655 if (l_backupAndRestoreCfgJsonObj.empty())
1656 {
1657 throw JsonException("JSON parsing failed",
1658 l_backupAndRestoreCfgFilePath);
1659 }
1660
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001661 // check if either of "source" or "destination" has inventory path.
1662 // this indicates that this sytem has System VPD on hardware
1663 // and other copy on D-Bus (BMC cache).
1664 if (!l_backupAndRestoreCfgJsonObj.empty() &&
1665 ((l_backupAndRestoreCfgJsonObj.contains("source") &&
1666 l_backupAndRestoreCfgJsonObj["source"].contains(
1667 "inventoryPath")) ||
1668 (l_backupAndRestoreCfgJsonObj.contains("destination") &&
1669 l_backupAndRestoreCfgJsonObj["destination"].contains(
1670 "inventoryPath"))))
1671 {
1672 BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
1673 auto [l_srcVpdVariant,
1674 l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
1675
1676 // ToDo: Revisit is this check is required or not.
1677 if (auto l_srcVpdMap =
1678 std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
1679 l_srcVpdMap && !(*l_srcVpdMap).empty())
1680 {
1681 io_srcVpdMap = std::move(l_srcVpdVariant);
1682 }
1683 }
1684 }
1685 catch (const std::exception& l_ex)
1686 {
1687 EventLogger::createSyncPel(
Sunny Srivastava043955d2025-01-21 18:04:49 +05301688 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301689 __FILE__, __FUNCTION__, 0,
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001690 std::string(
1691 "Exception caught while backup and restore VPD keyword's.") +
Sunny Srivastava15a189a2025-02-26 16:53:19 +05301692 EventLogger::getErrorMsg(l_ex),
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001693 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1694 }
1695}
1696
1697void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
1698{
1699 if (i_dbusObjPath.empty())
1700 {
1701 throw std::runtime_error("Given DBus object path is empty.");
1702 }
1703
1704 const std::string& l_fruPath =
1705 jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
1706
1707 try
1708 {
1709 auto l_presentPropValue = dbusUtility::readDbusProperty(
1710 constants::pimServiceName, i_dbusObjPath,
1711 constants::inventoryItemInf, "Present");
1712
1713 if (auto l_value = std::get_if<bool>(&l_presentPropValue))
1714 {
1715 if (!(*l_value))
1716 {
1717 throw std::runtime_error("Given FRU is not present");
1718 }
1719 else
1720 {
1721 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1722 "preAction", "deletion"))
1723 {
1724 if (!processPreAction(l_fruPath, "deletion"))
1725 {
1726 throw std::runtime_error("Pre action failed");
1727 }
1728 }
1729
1730 std::vector<std::string> l_interfaceList{
1731 constants::operationalStatusInf};
1732
1733 types::MapperGetSubTree l_subTreeMap =
1734 dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
1735 l_interfaceList);
1736
1737 types::ObjectMap l_objectMap;
1738
1739 // Updates VPD specific interfaces property value under PIM for
1740 // sub FRUs.
1741 for (const auto& [l_objectPath, l_serviceInterfaceMap] :
1742 l_subTreeMap)
1743 {
1744 types::InterfaceMap l_interfaceMap;
1745 vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
1746 l_interfaceMap);
1747 l_objectMap.emplace(l_objectPath,
1748 std::move(l_interfaceMap));
1749 }
1750
1751 types::InterfaceMap l_interfaceMap;
1752 vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
1753 l_interfaceMap);
1754
1755 l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
1756
1757 if (!dbusUtility::callPIM(std::move(l_objectMap)))
1758 {
1759 throw std::runtime_error("Call to PIM failed.");
1760 }
1761
1762 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1763 "postAction", "deletion"))
1764 {
1765 if (!processPostAction(l_fruPath, "deletion"))
1766 {
1767 throw std::runtime_error("Post action failed");
1768 }
1769 }
1770 }
1771 }
1772 else
1773 {
1774 logging::logMessage(
1775 "Can't process delete VPD for FRU [" + i_dbusObjPath +
1776 "] as unable to read present property");
1777 return;
1778 }
1779
1780 logging::logMessage(
1781 "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
1782 }
1783 catch (const std::exception& l_ex)
1784 {
1785 if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
1786 "postFailAction", "deletion"))
1787 {
1788 if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
1789 "deletion"))
1790 {
1791 logging::logMessage(
1792 "Post fail action failed for: " + i_dbusObjPath);
1793 }
1794 }
1795
1796 logging::logMessage("Failed to delete VPD for FRU : " + i_dbusObjPath +
1797 " error: " + std::string(l_ex.what()));
1798 }
1799}
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301800
1801void Worker::setPresentProperty(const std::string& i_vpdPath,
1802 const bool& i_value)
1803{
1804 try
1805 {
1806 if (i_vpdPath.empty())
1807 {
1808 throw std::runtime_error(
1809 "Path is empty. Can't set present property");
1810 }
1811
1812 types::ObjectMap l_objectInterfaceMap;
1813
1814 // If the given path is EEPROM path.
1815 if (m_parsedJson["frus"].contains(i_vpdPath))
1816 {
1817 for (const auto& l_Fru : m_parsedJson["frus"][i_vpdPath])
1818 {
1819 sdbusplus::message::object_path l_fruObjectPath(
1820 l_Fru["inventoryPath"]);
1821
1822 types::PropertyMap l_propertyValueMap;
1823 l_propertyValueMap.emplace("Present", i_value);
1824
1825 types::InterfaceMap l_interfaces;
1826 vpdSpecificUtility::insertOrMerge(l_interfaces,
1827 constants::inventoryItemInf,
1828 move(l_propertyValueMap));
1829
1830 l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
1831 std::move(l_interfaces));
1832 }
1833 }
1834 else
1835 {
1836 // consider it as an inventory path.
1837 if (i_vpdPath.find(constants::pimPath) != constants::VALUE_0)
1838 {
1839 throw std::runtime_error(
1840 "Invalid inventory path: " + i_vpdPath);
1841 }
1842
1843 types::PropertyMap l_propertyValueMap;
1844 l_propertyValueMap.emplace("Present", i_value);
1845
1846 types::InterfaceMap l_interfaces;
1847 vpdSpecificUtility::insertOrMerge(l_interfaces,
1848 constants::inventoryItemInf,
1849 move(l_propertyValueMap));
1850
1851 l_objectInterfaceMap.emplace(i_vpdPath, std::move(l_interfaces));
1852 }
1853
1854 // Notify PIM
1855 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
1856 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301857 throw DbusException(
1858 std::string(__FUNCTION__) +
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301859 "Call to PIM failed while setting present property for path " +
1860 i_vpdPath);
1861 }
1862 }
1863 catch (const std::exception& l_ex)
1864 {
Sunny Srivastava4c509c22025-03-25 12:43:40 +05301865 EventLogger::createSyncPel(
1866 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
1867 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
1868 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
Sunny Srivastavad159bb42025-01-09 11:13:50 +05301869 }
1870}
1871
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001872} // namespace vpd