1 #include "config.h"
2
3 #include "ibm_handler.hpp"
4
5 #include "configuration.hpp"
6 #include "listener.hpp"
7 #include "logger.hpp"
8 #include "parser.hpp"
9
10 #include <utility/common_utility.hpp>
11 #include <utility/dbus_utility.hpp>
12 #include <utility/json_utility.hpp>
13 #include <utility/vpd_specific_utility.hpp>
14
15 namespace vpd
16 {
IbmHandler(std::shared_ptr<Worker> & o_worker,std::shared_ptr<BackupAndRestore> & o_backupAndRestoreObj,const std::shared_ptr<sdbusplus::asio::dbus_interface> & i_iFace,const std::shared_ptr<sdbusplus::asio::dbus_interface> & i_progressiFace,const std::shared_ptr<boost::asio::io_context> & i_ioCon,const std::shared_ptr<sdbusplus::asio::connection> & i_asioConnection)17 IbmHandler::IbmHandler(
18 std::shared_ptr<Worker>& o_worker,
19 std::shared_ptr<BackupAndRestore>& o_backupAndRestoreObj,
20 const std::shared_ptr<sdbusplus::asio::dbus_interface>& i_iFace,
21 const std::shared_ptr<sdbusplus::asio::dbus_interface>& i_progressiFace,
22 const std::shared_ptr<boost::asio::io_context>& i_ioCon,
23 const std::shared_ptr<sdbusplus::asio::connection>& i_asioConnection) :
24 m_worker(o_worker), m_backupAndRestoreObj(o_backupAndRestoreObj),
25 m_interface(i_iFace), m_progressInterface(i_progressiFace),
26 m_ioContext(i_ioCon), m_asioConnection(i_asioConnection),
27 m_logger(Logger::getLoggerInstance())
28 {
29 try
30 {
31 readVpdCollectionMode();
32
33 // check if symlink is present
34 isSymlinkPresent();
35
36 // Set up minimal things that is needed before bus name is claimed.
37 performInitialSetup();
38
39 uint16_t l_errCode{0};
40 // If the object is created, implies back up and restore took place in
41 // system VPD flow.
42 if ((m_backupAndRestoreObj == nullptr) && !m_sysCfgJsonObj.empty() &&
43 jsonUtility::isBackupAndRestoreRequired(m_sysCfgJsonObj, l_errCode))
44 {
45 try
46 {
47 m_backupAndRestoreObj =
48 std::make_shared<BackupAndRestore>(m_sysCfgJsonObj);
49 }
50 catch (const std::exception& l_ex)
51 {
52 logging::logMessage(
53 "Back up and restore instantiation failed. {" +
54 std::string(l_ex.what()) + "}");
55
56 EventLogger::createSyncPel(
57 EventLogger::getErrorType(l_ex),
58 types::SeverityType::Warning, __FILE__, __FUNCTION__, 0,
59 EventLogger::getErrorMsg(l_ex), std::nullopt, std::nullopt,
60 std::nullopt, std::nullopt);
61 }
62 }
63 else if (l_errCode)
64 {
65 logging::logMessage(
66 "Failed to check if backup & restore required. Error : " +
67 commonUtility::getErrCodeMsg(l_errCode));
68 }
69
70 // Instantiate Listener object
71 m_eventListener =
72 std::make_shared<Listener>(m_worker, m_asioConnection);
73 m_eventListener->registerAssetTagChangeCallback();
74 m_eventListener->registerHostStateChangeCallback();
75 m_eventListener->registerPresenceChangeCallback();
76
77 // Instantiate GpioMonitor class
78 m_gpioMonitor = std::make_shared<GpioMonitor>(m_sysCfgJsonObj, m_worker,
79 m_ioContext);
80 }
81 catch (const std::exception& l_ec)
82 {
83 // PEL must have been logged if the code is at this point. So no need to
84 // log again. Let the service continue to execute.
85 m_logger->logMessage("IBM Handler instantiation failed. Reason: " +
86 std::string(l_ec.what()));
87 }
88 }
89
readVpdCollectionMode()90 void IbmHandler::readVpdCollectionMode() noexcept
91 {
92 uint16_t l_errCode{0};
93 // check VPD collection mode
94 if (!commonUtility::isFieldModeEnabled(l_errCode))
95 {
96 if (l_errCode)
97 {
98 m_logger->logMessage(
99 "Default mode set. Error while trying to check if field mode is enabled, error : " +
100 commonUtility::getErrCodeMsg(l_errCode));
101
102 return;
103 }
104
105 m_vpdCollectionMode = commonUtility::getVpdCollectionMode(l_errCode);
106
107 if (l_errCode)
108 {
109 m_logger->logMessage(
110 "Default mode set. Error while trying to read VPD collection mode: " +
111 commonUtility::getErrCodeMsg(l_errCode));
112 }
113 }
114 }
115
isSymlinkPresent()116 void IbmHandler::isSymlinkPresent() noexcept
117 {
118 // Check if symlink is already there to confirm fresh boot/factory reset.
119 std::error_code l_ec;
120 if (!std::filesystem::exists(INVENTORY_JSON_SYM_LINK, l_ec))
121 {
122 if (l_ec)
123 {
124 m_logger->logMessage(
125 "Error reading symlink location. Reason: " + l_ec.message());
126 }
127
128 if (dbusUtility::isChassisPowerOn())
129 {
130 // Predictive PEL logged. Symlink can't go missing while chassis
131 // is on as system VPD will not get processed in chassis on state.
132 types::PelInfoTuple l_pel(
133 types::ErrorType::FirmwareError, types::SeverityType::Warning,
134 0, std::nullopt, std::nullopt, std::nullopt, std::nullopt);
135
136 m_logger->logMessage(
137 std::string(
138 "Error reading config JSON symlink in chassis on state."),
139 PlaceHolder::PEL, &l_pel);
140 }
141 return;
142 }
143
144 m_logger->logMessage("Sym Link present.");
145
146 // update JSON path to symlink path.
147 m_configJsonPath = INVENTORY_JSON_SYM_LINK;
148 m_isSymlinkPresent = true;
149 }
150
initWorker()151 void IbmHandler::initWorker()
152 {
153 try
154 {
155 // At power on, less number of FRU(s) needs collection. Hence defaulted
156 // to 1.
157 uint8_t l_threadCount = constants::VALUE_1;
158 if (!dbusUtility::isChassisPowerOn())
159 {
160 // TODO: Can be configured from recipe? Check.
161 l_threadCount = constants::MAX_THREADS;
162 }
163
164 // Initialize worker with required parameters.
165 m_worker = std::make_shared<Worker>(m_configJsonPath, l_threadCount,
166 m_vpdCollectionMode);
167 }
168 catch (const std::exception& l_ex)
169 {
170 // Critical PEL logged as collection can't progress without worker
171 // object.
172 const types::PelInfoTuple l_pel(
173 EventLogger::getErrorType(l_ex), types::SeverityType::Critical, 0,
174 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
175
176 m_logger->logMessage(
177 std::string("Exception while creating worker object") +
178 EventLogger::getErrorMsg(l_ex),
179 PlaceHolder::PEL, &l_pel);
180
181 // Throwing error back to avoid any further processing.
182 throw std::runtime_error(
183 std::string("Exception while creating worker object") +
184 EventLogger::getErrorMsg(l_ex));
185 }
186 }
187
SetTimerToDetectVpdCollectionStatus()188 void IbmHandler::SetTimerToDetectVpdCollectionStatus()
189 {
190 // Keeping max retry for 2 minutes. TODO: Make it configurable based on
191 // system type.
192 static constexpr auto MAX_RETRY = 12;
193
194 static boost::asio::steady_timer l_timer(*m_ioContext);
195 static uint8_t l_timerRetry = 0;
196
197 auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(10));
198
199 (l_asyncCancelled == 0)
200 ? logging::logMessage("Collection Timer started")
201 : logging::logMessage("Collection Timer re-started");
202
203 l_timer.async_wait([this](const boost::system::error_code& ec) {
204 if (ec == boost::asio::error::operation_aborted)
205 {
206 throw std::runtime_error(
207 "Timer to detect thread collection status was aborted");
208 }
209
210 if (ec)
211 {
212 throw std::runtime_error(
213 "Timer to detect thread collection failed");
214 }
215
216 if (m_worker->isAllFruCollectionDone())
217 {
218 // cancel the timer
219 l_timer.cancel();
220 processFailedEeproms();
221
222 // update VPD for powerVS system.
223 ConfigurePowerVsSystem();
224
225 m_logger->logMessage("m_worker->isSystemVPDOnDBus() completed");
226
227 m_progressInterface->set_property(
228 "Status", std::string(constants::vpdCollectionCompleted));
229
230 if (m_backupAndRestoreObj)
231 {
232 m_backupAndRestoreObj->backupAndRestore();
233 }
234
235 if (m_eventListener)
236 {
237 // Check if system config JSON specifies
238 // correlatedPropertiesJson
239 if (m_sysCfgJsonObj.contains("correlatedPropertiesConfigPath"))
240 {
241 // register correlated properties callback with specific
242 // correlated properties JSON
243 m_eventListener->registerCorrPropCallBack(
244 m_sysCfgJsonObj["correlatedPropertiesConfigPath"]);
245 }
246 else
247 {
248 m_logger->logMessage(
249 "Correlated properties JSON path is not defined in system config JSON. Correlated properties listener is disabled.");
250 }
251 }
252 #ifdef ENABLE_FILE_LOGGING
253 // terminate collection logger
254 m_logger->terminateVpdCollectionLogging();
255 #endif
256 }
257 else
258 {
259 auto l_threadCount = m_worker->getActiveThreadCount();
260 if (l_timerRetry == MAX_RETRY)
261 {
262 l_timer.cancel();
263 logging::logMessage("Taking too long. Active thread = " +
264 std::to_string(l_threadCount));
265 #ifdef ENABLE_FILE_LOGGING
266 // terminate collection logger
267 m_logger->terminateVpdCollectionLogging();
268 #endif
269 }
270 else
271 {
272 l_timerRetry++;
273 logging::logMessage("Collection is in progress for [" +
274 std::to_string(l_threadCount) + "] FRUs.");
275
276 SetTimerToDetectVpdCollectionStatus();
277 }
278 }
279 });
280 }
281
checkAndUpdatePowerVsVpd(const nlohmann::json & i_powerVsJsonObj,std::vector<std::string> & o_failedPathList)282 void IbmHandler::checkAndUpdatePowerVsVpd(
283 const nlohmann::json& i_powerVsJsonObj,
284 std::vector<std::string>& o_failedPathList)
285 {
286 for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items())
287 {
288 nlohmann::json l_sysCfgJsonObj{};
289 if (m_worker.get() != nullptr)
290 {
291 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
292 }
293
294 // The utility method will handle emty JSON case. No explicit
295 // handling required here.
296 uint16_t l_errCode = 0;
297 auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson(
298 l_sysCfgJsonObj, l_fruPath, l_errCode);
299
300 // Mark it as failed if inventory path not found in JSON.
301 if (l_inventoryPath.empty())
302 {
303 if (l_errCode)
304 {
305 logging::logMessage(
306 "Failed to get inventory object path from JSON for FRU [" +
307 l_fruPath +
308 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
309 }
310
311 o_failedPathList.push_back(l_fruPath);
312 continue;
313 }
314
315 // check if the FRU is present
316 if (!dbusUtility::isInventoryPresent(l_inventoryPath))
317 {
318 logging::logMessage(
319 "Inventory not present, skip updating part number. Path: " +
320 l_inventoryPath);
321 continue;
322 }
323
324 // check if the FRU needs CCIN check before updating PN.
325 if (l_recJson.contains("CCIN"))
326 {
327 const auto& l_ccinFromDbus =
328 vpdSpecificUtility::getCcinFromDbus(l_inventoryPath, l_errCode);
329
330 // Not an ideal situation as CCIN can't be empty.
331 if (l_ccinFromDbus.empty())
332 {
333 if (l_errCode)
334 {
335 m_logger->logMessage(
336 "Failed to get CCIN value from DBus, error : " +
337 commonUtility::getErrCodeMsg(l_errCode));
338 }
339
340 o_failedPathList.push_back(l_fruPath);
341 continue;
342 }
343
344 std::vector<std::string> l_ccinListFromJson = l_recJson["CCIN"];
345
346 if (find(l_ccinListFromJson.begin(), l_ccinListFromJson.end(),
347 l_ccinFromDbus) == l_ccinListFromJson.end())
348 {
349 // Don't update PN in this case.
350 continue;
351 }
352 }
353
354 for (const auto& [l_recordName, l_kwdJson] : l_recJson.items())
355 {
356 // Record name can't be CCIN, skip processing as it is there for PN
357 // update based on CCIN check.
358 if (l_recordName == constants::kwdCCIN)
359 {
360 continue;
361 }
362
363 for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items())
364 {
365 // Is value of type array.
366 if (!l_kwdValue.is_array())
367 {
368 o_failedPathList.push_back(l_fruPath);
369 continue;
370 }
371
372 // Get current FRU Part number.
373 auto l_retVal = dbusUtility::readDbusProperty(
374 constants::pimServiceName, l_inventoryPath,
375 constants::viniInf, constants::kwdFN);
376
377 auto l_ptrToFn = std::get_if<types::BinaryVector>(&l_retVal);
378
379 if (!l_ptrToFn)
380 {
381 o_failedPathList.push_back(l_fruPath);
382 continue;
383 }
384
385 types::BinaryVector l_binaryKwdValue =
386 l_kwdValue.get<types::BinaryVector>();
387 if (l_binaryKwdValue == (*l_ptrToFn))
388 {
389 continue;
390 }
391
392 // Update part number only if required.
393 std::shared_ptr<Parser> l_parserObj =
394 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
395 if (l_parserObj->updateVpdKeyword(std::make_tuple(
396 l_recordName, l_kwdName, l_binaryKwdValue)) ==
397 constants::FAILURE)
398 {
399 o_failedPathList.push_back(l_fruPath);
400 continue;
401 }
402
403 // update the Asset interface Spare part number explicitly.
404 if (!dbusUtility::callPIM(types::ObjectMap{
405 {l_inventoryPath,
406 {{constants::assetInf,
407 {{"SparePartNumber",
408 std::string(l_binaryKwdValue.begin(),
409 l_binaryKwdValue.end())}}}}}}))
410 {
411 logging::logMessage(
412 "Updating Spare Part Number under Asset interface failed for path [" +
413 l_inventoryPath + "]");
414 }
415
416 // Just needed for logging.
417 std::string l_initialPartNum((*l_ptrToFn).begin(),
418 (*l_ptrToFn).end());
419 std::string l_finalPartNum(l_binaryKwdValue.begin(),
420 l_binaryKwdValue.end());
421 logging::logMessage(
422 "FRU Part number updated for path [" + l_inventoryPath +
423 "]" + "From [" + l_initialPartNum + "]" + " to [" +
424 l_finalPartNum + "]");
425 }
426 }
427 }
428 }
429
ConfigurePowerVsSystem()430 void IbmHandler::ConfigurePowerVsSystem()
431 {
432 std::vector<std::string> l_failedPathList;
433 try
434 {
435 types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
436 if (l_imValue.empty())
437 {
438 throw DbusException("Invalid IM value read from Dbus");
439 }
440
441 uint16_t l_errCode = 0;
442 if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue, l_errCode))
443 {
444 // TODO: Should booting be blocked in case of some
445 // misconfigurations?
446 if (l_errCode)
447 {
448 logging::logMessage(
449 "Failed to check if the system is powerVs Configuration, error : " +
450 commonUtility::getErrCodeMsg(l_errCode));
451 }
452
453 return;
454 }
455
456 const nlohmann::json& l_powerVsJsonObj =
457 jsonUtility::getPowerVsJson(l_imValue, l_errCode);
458
459 if (l_powerVsJsonObj.empty())
460 {
461 throw std::runtime_error("PowerVS Json not found. Error : " +
462 commonUtility::getErrCodeMsg(l_errCode));
463 }
464
465 checkAndUpdatePowerVsVpd(l_powerVsJsonObj, l_failedPathList);
466
467 if (!l_failedPathList.empty())
468 {
469 throw std::runtime_error(
470 "Part number update failed for following paths: ");
471 }
472 }
473 catch (const std::exception& l_ex)
474 {
475 // TODO log appropriate PEL
476 }
477 }
478
processFailedEeproms()479 void IbmHandler::processFailedEeproms()
480 {
481 if (m_worker.get() != nullptr)
482 {
483 // TODO:
484 // - iterate through list of EEPROMs for which thread creation has
485 // failed
486 // - For each failed EEPROM, trigger VPD collection
487 m_worker->getFailedEepromPaths().clear();
488 }
489 }
490
enableMuxChips()491 void IbmHandler::enableMuxChips()
492 {
493 if (m_sysCfgJsonObj.empty())
494 {
495 // config JSON should not be empty at this point of execution.
496 throw std::runtime_error("Config JSON is empty. Can't enable muxes");
497 return;
498 }
499
500 if (!m_sysCfgJsonObj.contains("muxes"))
501 {
502 logging::logMessage("No mux defined for the system in config JSON");
503 return;
504 }
505
506 // iterate over each MUX detail and enable them.
507 for (const auto& item : m_sysCfgJsonObj["muxes"])
508 {
509 uint16_t l_errCode = 0;
510 if (item.contains("holdidlepath"))
511 {
512 std::string cmd = "echo 0 > ";
513 cmd += item["holdidlepath"];
514
515 logging::logMessage("Enabling mux with command = " + cmd);
516
517 commonUtility::executeCmd(cmd, l_errCode);
518
519 if (l_errCode)
520 {
521 m_logger->logMessage(
522 "Failed to execute command [" + cmd +
523 "], error : " + commonUtility::getErrCodeMsg(l_errCode));
524 }
525
526 continue;
527 }
528
529 logging::logMessage(
530 "Mux Entry does not have hold idle path. Can't enable the mux");
531 }
532 }
533
getSystemJson(std::string & o_systemJson,const types::VPDMapVariant & i_parsedVpdMap)534 void IbmHandler::getSystemJson(std::string& o_systemJson,
535 const types::VPDMapVariant& i_parsedVpdMap)
536 {
537 if (auto l_pVal = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
538 {
539 uint16_t l_errCode = 0;
540 std::string l_hwKWdValue =
541 vpdSpecificUtility::getHWVersion(*l_pVal, l_errCode);
542 if (l_hwKWdValue.empty())
543 {
544 if (l_errCode)
545 {
546 throw DataException("Failed to fetch HW value. Reason: " +
547 commonUtility::getErrCodeMsg(l_errCode));
548 }
549 throw DataException("HW value fetched is empty.");
550 }
551
552 const std::string& l_imKwdValue =
553 vpdSpecificUtility::getIMValue(*l_pVal, l_errCode);
554 if (l_imKwdValue.empty())
555 {
556 if (l_errCode)
557 {
558 throw DataException("Failed to fetch IM value. Reason: " +
559 commonUtility::getErrCodeMsg(l_errCode));
560 }
561 throw DataException("IM value fetched is empty.");
562 }
563
564 auto l_itrToIM = config::systemType.find(l_imKwdValue);
565 if (l_itrToIM == config::systemType.end())
566 {
567 throw DataException("IM keyword does not map to any system type");
568 }
569
570 const types::HWVerList l_hwVersionList = l_itrToIM->second.second;
571 if (!l_hwVersionList.empty())
572 {
573 transform(l_hwKWdValue.begin(), l_hwKWdValue.end(),
574 l_hwKWdValue.begin(), ::toupper);
575
576 auto l_itrToHW =
577 std::find_if(l_hwVersionList.begin(), l_hwVersionList.end(),
578 [&l_hwKWdValue](const auto& l_aPair) {
579 return l_aPair.first == l_hwKWdValue;
580 });
581
582 if (l_itrToHW != l_hwVersionList.end())
583 {
584 if (!(*l_itrToHW).second.empty())
585 {
586 o_systemJson += (*l_itrToIM).first + "_" +
587 (*l_itrToHW).second + ".json";
588 }
589 else
590 {
591 o_systemJson += (*l_itrToIM).first + ".json";
592 }
593 return;
594 }
595 }
596 o_systemJson += l_itrToIM->second.first + ".json";
597 return;
598 }
599
600 throw DataException(
601 "Invalid VPD type returned from Parser. Can't get system JSON.");
602 }
603
setEnvAndReboot(const std::string & i_key,const std::string & i_value)604 void IbmHandler::setEnvAndReboot(const std::string& i_key,
605 const std::string& i_value)
606 {
607 // set env and reboot and break.
608 uint16_t l_errCode = 0;
609 commonUtility::executeCmd("/sbin/fw_setenv", l_errCode, i_key, i_value);
610
611 if (l_errCode)
612 {
613 throw std::runtime_error(
614 "Failed to execute command [/sbin/fw_setenv " + i_key + " " +
615 i_value + "], error : " + commonUtility::getErrCodeMsg(l_errCode));
616 }
617
618 #ifdef SKIP_REBOOT_ON_FITCONFIG_CHANGE
619 m_logger->logMessage("NOT Rebooting BMC to pick up new device tree");
620 #else
621 m_logger->logMessage("Rebooting BMC to pick up new device tree");
622
623 // make dbus call to reboot
624 auto l_bus = sdbusplus::bus::new_default_system();
625 auto l_method = l_bus.new_method_call(
626 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
627 "org.freedesktop.systemd1.Manager", "Reboot");
628 l_bus.call_noreply(l_method);
629 exit(EXIT_SUCCESS);
630 #endif
631 }
632
readFitConfigValue()633 std::string IbmHandler::readFitConfigValue()
634 {
635 uint16_t l_errCode = 0;
636 std::vector<std::string> l_output =
637 commonUtility::executeCmd("/sbin/fw_printenv", l_errCode);
638
639 if (l_errCode)
640 {
641 m_logger->logMessage(
642 "Failed to execute command [/sbin/fw_printenv], error : " +
643 commonUtility::getErrCodeMsg(l_errCode));
644 }
645
646 std::string l_fitConfigValue;
647
648 for (const auto& l_entry : l_output)
649 {
650 auto l_pos = l_entry.find("=");
651 auto l_key = l_entry.substr(0, l_pos);
652 if (l_key != "fitconfig")
653 {
654 continue;
655 }
656
657 if (l_pos + 1 < l_entry.size())
658 {
659 l_fitConfigValue = l_entry.substr(l_pos + 1);
660 }
661 }
662
663 return l_fitConfigValue;
664 }
665
isBackupOnCache()666 bool IbmHandler::isBackupOnCache()
667 {
668 try
669 {
670 uint16_t l_errCode = 0;
671 std::string l_backupAndRestoreCfgFilePath =
672 m_sysCfgJsonObj.value("backupRestoreConfigPath", "");
673
674 if (l_backupAndRestoreCfgFilePath.empty())
675 {
676 m_logger->logMessage(
677 "backupRestoreConfigPath is not found in JSON. Can't determne the backup path.");
678 return false;
679 }
680
681 nlohmann::json l_backupAndRestoreCfgJsonObj =
682 jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath,
683 l_errCode);
684 if (l_backupAndRestoreCfgJsonObj.empty() || l_errCode)
685 {
686 m_logger->logMessage(
687 "JSON parsing failed for file [ " +
688 std::string(l_backupAndRestoreCfgFilePath) +
689 " ], error : " + commonUtility::getErrCodeMsg(l_errCode));
690 return false;
691 }
692
693 // check if either of "source" or "destination" has inventory path.
694 // this indicates that this sytem has System VPD on hardware
695 // and other copy on D-Bus (BMC cache).
696 if (!l_backupAndRestoreCfgJsonObj.empty() &&
697 ((l_backupAndRestoreCfgJsonObj.contains("source") &&
698 l_backupAndRestoreCfgJsonObj["source"].contains(
699 "inventoryPath")) ||
700 (l_backupAndRestoreCfgJsonObj.contains("destination") &&
701 l_backupAndRestoreCfgJsonObj["destination"].contains(
702 "inventoryPath"))))
703 {
704 return true;
705 }
706 }
707 catch (const std::exception& l_ex)
708 {
709 m_logger->logMessage(
710 "Exception while checking for backup on cache. Reason:" +
711 std::string(l_ex.what()));
712 }
713
714 // In case of any failure/ambiguity. Don't perform back up and restore.
715 return false;
716 }
717
performBackupAndRestore(types::VPDMapVariant & io_srcVpdMap)718 void IbmHandler::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
719 {
720 try
721 {
722 m_backupAndRestoreObj =
723 std::make_shared<BackupAndRestore>(m_sysCfgJsonObj);
724 auto [l_srcVpdVariant,
725 l_dstVpdVariant] = m_backupAndRestoreObj->backupAndRestore();
726
727 // ToDo: Revisit is this check is required or not.
728 if (auto l_srcVpdMap = std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
729 l_srcVpdMap && !(*l_srcVpdMap).empty())
730 {
731 io_srcVpdMap = std::move(l_srcVpdVariant);
732 }
733 }
734 catch (const std::exception& l_ex)
735 {
736 EventLogger::createSyncPel(
737 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
738 __FILE__, __FUNCTION__, 0,
739 std::string(
740 "Exception caught while backup and restore VPD keyword's.") +
741 EventLogger::getErrorMsg(l_ex),
742 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
743 }
744 }
745
createAssetTagString(const types::VPDMapVariant & i_parsedVpdMap)746 std::string IbmHandler::createAssetTagString(
747 const types::VPDMapVariant& i_parsedVpdMap)
748 {
749 std::string l_assetTag;
750 // system VPD will be in IPZ format.
751 if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
752 {
753 auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
754 if (l_itrToVsys != (*l_parsedVpdMap).end())
755 {
756 uint16_t l_errCode = 0;
757 const std::string l_tmKwdValue{vpdSpecificUtility::getKwVal(
758 l_itrToVsys->second, constants::kwdTM, l_errCode)};
759 if (l_tmKwdValue.empty())
760 {
761 throw std::runtime_error(
762 std::string("Failed to get value for keyword [") +
763 constants::kwdTM +
764 std::string("] while creating Asset tag. Error : " +
765 commonUtility::getErrCodeMsg(l_errCode)));
766 }
767 const std::string l_seKwdValue{vpdSpecificUtility::getKwVal(
768 l_itrToVsys->second, constants::kwdSE, l_errCode)};
769 if (l_seKwdValue.empty())
770 {
771 throw std::runtime_error(
772 std::string("Failed to get value for keyword [") +
773 constants::kwdSE +
774 std::string("] while creating Asset tag. Error : " +
775 commonUtility::getErrCodeMsg(l_errCode)));
776 }
777 l_assetTag = std::string{"Server-"} + l_tmKwdValue +
778 std::string{"-"} + l_seKwdValue;
779 }
780 else
781 {
782 throw std::runtime_error(
783 "VSYS record not found in parsed VPD map to create Asset tag.");
784 }
785 }
786 else
787 {
788 throw std::runtime_error(
789 "Invalid VPD type recieved to create Asset tag.");
790 }
791 return l_assetTag;
792 }
793
publishSystemVPD(const types::VPDMapVariant & i_parsedVpdMap)794 void IbmHandler::publishSystemVPD(const types::VPDMapVariant& i_parsedVpdMap)
795 {
796 types::ObjectMap l_objectInterfaceMap;
797 if (std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
798 {
799 m_worker->populateDbus(i_parsedVpdMap, l_objectInterfaceMap,
800 SYSTEM_VPD_FILE_PATH);
801 try
802 {
803 if (m_isFactoryResetDone)
804 {
805 const auto& l_assetTag = createAssetTagString(i_parsedVpdMap);
806 auto l_itrToSystemPath = l_objectInterfaceMap.find(
807 sdbusplus::message::object_path(constants::systemInvPath));
808 if (l_itrToSystemPath == l_objectInterfaceMap.end())
809 {
810 throw std::runtime_error(
811 "Asset tag update failed. System Path not found in object map.");
812 }
813 types::PropertyMap l_assetTagProperty;
814 l_assetTagProperty.emplace("AssetTag", l_assetTag);
815 (l_itrToSystemPath->second)
816 .emplace(constants::assetTagInf,
817 std::move(l_assetTagProperty));
818 }
819 }
820 catch (const std::exception& l_ex)
821 {
822 EventLogger::createSyncPel(
823 EventLogger::getErrorType(l_ex), types::SeverityType::Warning,
824 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex),
825 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
826 }
827 // Notify PIM
828 if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
829 {
830 throw std::runtime_error("Call to PIM failed for system VPD");
831 }
832 }
833 else
834 {
835 throw DataException("Invalid format of parsed VPD map.");
836 }
837 }
838
setJsonSymbolicLink(const std::string & i_systemJson)839 void IbmHandler::setJsonSymbolicLink(const std::string& i_systemJson)
840 {
841 std::error_code l_ec;
842 l_ec.clear();
843
844 // Check if symlink file path exists and if the JSON at this location is a
845 // symlink.
846 if (m_isSymlinkPresent &&
847 std::filesystem::is_symlink(INVENTORY_JSON_SYM_LINK, l_ec))
848 {
849 // Don't care about exception in "is_symlink". Will continue with
850 // creationof symlink.
851 const auto& l_symlinkFilePth =
852 std::filesystem::read_symlink(INVENTORY_JSON_SYM_LINK, l_ec);
853 if (l_ec)
854 {
855 logging::logMessage(
856 "Can't read existing symlink. Error =" + l_ec.message() +
857 "Trying removal of symlink and creation of new symlink.");
858 }
859
860 // If currently set JSON is the required one. No further processing
861 // required.
862 if (i_systemJson == l_symlinkFilePth)
863 {
864 // Correct symlink is already set.
865 return;
866 }
867
868 if (!std::filesystem::remove(INVENTORY_JSON_SYM_LINK, l_ec))
869 {
870 // No point going further. If removal fails for existing symlink,
871 // create will anyways throw.
872 throw std::runtime_error(
873 "Removal of symlink failed with Error = " + l_ec.message() +
874 ". Can't proceed with create_symlink.");
875 }
876 }
877
878 if (!std::filesystem::exists(VPD_SYMLIMK_PATH, l_ec))
879 {
880 if (l_ec)
881 {
882 throw std::runtime_error(
883 "File system call to exist failed with error = " +
884 l_ec.message());
885 }
886
887 // implies it is a fresh boot/factory reset.
888 // Create the directory for hosting the symlink
889 if (!std::filesystem::create_directories(VPD_SYMLIMK_PATH, l_ec))
890 {
891 if (l_ec)
892 {
893 throw std::runtime_error(
894 "File system call to create directory failed with error = " +
895 l_ec.message());
896 }
897 }
898 }
899
900 // create a new symlink based on the system
901 std::filesystem::create_symlink(i_systemJson, INVENTORY_JSON_SYM_LINK,
902 l_ec);
903 if (l_ec)
904 {
905 throw std::runtime_error(
906 "create_symlink system call failed with error: " + l_ec.message());
907 }
908
909 // update path to symlink.
910 m_configJsonPath = INVENTORY_JSON_SYM_LINK;
911 m_isSymlinkPresent = true;
912
913 // If the flow is at this point implies the symlink was not present there.
914 // Considering this as factory reset.
915 m_isFactoryResetDone = true;
916 }
917
setDeviceTreeAndJson(types::VPDMapVariant & o_parsedSystemVpdMap)918 void IbmHandler::setDeviceTreeAndJson(
919 types::VPDMapVariant& o_parsedSystemVpdMap)
920 {
921 // JSON is madatory for processing of this API.
922 if (m_sysCfgJsonObj.empty())
923 {
924 throw JsonException("System config JSON is empty", m_sysCfgJsonObj);
925 }
926
927 uint16_t l_errCode = 0;
928 std::string l_systemVpdPath{SYSTEM_VPD_FILE_PATH};
929 commonUtility::getEffectiveFruPath(m_vpdCollectionMode, l_systemVpdPath,
930 l_errCode);
931
932 if (l_errCode)
933 {
934 throw std::runtime_error(
935 "Failed to get effective System VPD path, for [" + l_systemVpdPath +
936 "], reason: " + commonUtility::getErrCodeMsg(l_errCode));
937 }
938
939 std::error_code l_ec;
940 l_ec.clear();
941 if (!std::filesystem::exists(l_systemVpdPath, l_ec))
942 {
943 std::string l_errMsg = "Can't Find System VPD file/eeprom. ";
944 if (l_ec)
945 {
946 l_errMsg += l_ec.message();
947 }
948
949 // No point continuing without system VPD file
950 throw std::runtime_error(l_errMsg);
951 }
952
953 // parse system VPD
954 std::shared_ptr<Parser> l_vpdParser =
955 std::make_shared<Parser>(l_systemVpdPath, m_sysCfgJsonObj);
956 o_parsedSystemVpdMap = l_vpdParser->parse();
957
958 if (std::holds_alternative<std::monostate>(o_parsedSystemVpdMap))
959 {
960 throw std::runtime_error(
961 "System VPD parsing failed, from path [" + l_systemVpdPath +
962 "]. Either file doesn't exist or error occurred while parsing the file.");
963 }
964
965 // Implies it is default JSON.
966 std::string l_systemJson{JSON_ABSOLUTE_PATH_PREFIX};
967
968 // get system JSON as per the system configuration.
969 getSystemJson(l_systemJson, o_parsedSystemVpdMap);
970
971 if (!l_systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX))
972 {
973 throw DataException(
974 "No system JSON found corresponding to IM read from VPD.");
975 }
976
977 // re-parse the JSON once appropriate JSON has been selected.
978 m_sysCfgJsonObj = jsonUtility::getParsedJson(l_systemJson, l_errCode);
979
980 if (l_errCode)
981 {
982 throw(JsonException(
983 "JSON parsing failed for file [ " + l_systemJson +
984 " ], error : " + commonUtility::getErrCodeMsg(l_errCode),
985 l_systemJson));
986 }
987
988 vpdSpecificUtility::setCollectionStatusProperty(
989 SYSTEM_VPD_FILE_PATH, types::VpdCollectionStatus::InProgress,
990 m_sysCfgJsonObj, l_errCode);
991
992 if (l_errCode)
993 {
994 m_logger->logMessage("Failed to set collection status for path " +
995 std::string(SYSTEM_VPD_FILE_PATH) + "Reason: " +
996 commonUtility::getErrCodeMsg(l_errCode));
997 }
998
999 std::string l_devTreeFromJson;
1000 if (m_sysCfgJsonObj.contains("devTree"))
1001 {
1002 l_devTreeFromJson = m_sysCfgJsonObj["devTree"];
1003
1004 if (l_devTreeFromJson.empty())
1005 {
1006 EventLogger::createSyncPel(
1007 types::ErrorType::JsonFailure, types::SeverityType::Error,
1008 __FILE__, __FUNCTION__, 0,
1009 "Mandatory value for device tree missing from JSON[" +
1010 l_systemJson + "]",
1011 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1012 }
1013 }
1014
1015 auto l_fitConfigVal = readFitConfigValue();
1016
1017 if (l_devTreeFromJson.empty() ||
1018 l_fitConfigVal.find(l_devTreeFromJson) != std::string::npos)
1019 { // Skipping setting device tree as either devtree info is missing from
1020 // Json or it is rightly set.
1021
1022 setJsonSymbolicLink(l_systemJson);
1023
1024 const std::string& l_systemVpdInvPath =
1025 jsonUtility::getInventoryObjPathFromJson(
1026 m_sysCfgJsonObj, SYSTEM_VPD_FILE_PATH, l_errCode);
1027
1028 if (l_systemVpdInvPath.empty())
1029 {
1030 if (l_errCode)
1031 {
1032 throw JsonException(
1033 "System vpd inventory path not found in JSON. Reason:" +
1034 commonUtility::getErrCodeMsg(l_errCode),
1035 INVENTORY_JSON_SYM_LINK);
1036 }
1037 throw JsonException("System vpd inventory path is missing in JSON",
1038 INVENTORY_JSON_SYM_LINK);
1039 }
1040
1041 // TODO: for backward compatibility this should also support motherboard
1042 // interface.
1043 std::vector<std::string> l_interfaceList{
1044 constants::motherboardInterface};
1045 const types::MapperGetObject& l_sysVpdObjMap =
1046 dbusUtility::getObjectMap(l_systemVpdInvPath, l_interfaceList);
1047
1048 if (!l_sysVpdObjMap.empty())
1049 {
1050 if (isBackupOnCache() && jsonUtility::isBackupAndRestoreRequired(
1051 m_sysCfgJsonObj, l_errCode))
1052 {
1053 performBackupAndRestore(o_parsedSystemVpdMap);
1054 }
1055 else if (l_errCode)
1056 {
1057 logging::logMessage(
1058 "Failed to check if backup and restore required. Reason : " +
1059 commonUtility::getErrCodeMsg(l_errCode));
1060 }
1061 }
1062 return;
1063 }
1064
1065 setEnvAndReboot("fitconfig", l_devTreeFromJson);
1066 #ifdef SKIP_REBOOT_ON_FITCONFIG_CHANGE
1067 setJsonSymbolicLink(l_systemJson);
1068 #endif
1069 }
1070
performInitialSetup()1071 void IbmHandler::performInitialSetup()
1072 {
1073 // Parse whatever JSON is set as of now.
1074 uint16_t l_errCode = 0;
1075 try
1076 {
1077 m_sysCfgJsonObj =
1078 jsonUtility::getParsedJson(m_configJsonPath, l_errCode);
1079
1080 if (l_errCode)
1081 {
1082 // Throwing as there is no point proceeding without any JSON.
1083 throw JsonException("JSON parsing failed. error : " +
1084 commonUtility::getErrCodeMsg(l_errCode),
1085 m_configJsonPath);
1086 }
1087
1088 types::VPDMapVariant l_parsedSysVpdMap;
1089 setDeviceTreeAndJson(l_parsedSysVpdMap);
1090
1091 // now that correct JSON is selected, initialize worker class.
1092 initWorker();
1093
1094 // proceed to publish system VPD.
1095 publishSystemVPD(l_parsedSysVpdMap);
1096
1097 vpdSpecificUtility::setCollectionStatusProperty(
1098 SYSTEM_VPD_FILE_PATH, types::VpdCollectionStatus::Completed,
1099 m_sysCfgJsonObj, l_errCode);
1100
1101 if (l_errCode)
1102 {
1103 m_logger->logMessage(
1104 "Failed to set collection status for path " +
1105 std::string(SYSTEM_VPD_FILE_PATH) +
1106 "Reason: " + commonUtility::getErrCodeMsg(l_errCode));
1107 }
1108
1109 // Update BMC postion for RBMC prototype system
1110 // Ignore BMC position update in case of any error
1111 if (isRbmcPrototypeSystem(l_errCode))
1112 {
1113 size_t l_bmcPosition = std::numeric_limits<size_t>::max();
1114 checkAndUpdateBmcPosition(l_bmcPosition);
1115
1116 if (dbusUtility::callPIM(types::ObjectMap{
1117 {sdbusplus::message::object_path(constants::systemInvPath),
1118 {{constants::rbmcPositionInterface,
1119 {{"Position", l_bmcPosition}}}}}}))
1120 {
1121 m_logger->logMessage(
1122 "Updating BMC position failed for path [" +
1123 std::string(constants::systemInvPath) +
1124 "], bmc position: " + std::to_string(l_bmcPosition));
1125
1126 // ToDo: Check is PEL required
1127 }
1128 }
1129 else if (l_errCode != 0)
1130 {
1131 m_logger->logMessage(
1132 "Unable to determine whether system is RBMC system or not, reason: " +
1133 commonUtility::getErrCodeMsg(l_errCode));
1134 }
1135
1136 // Enable all mux which are used for connecting to the i2c on the
1137 // pcie slots for pcie cards. These are not enabled by kernel due to
1138 // an issue seen with Castello cards, where the i2c line hangs on a
1139 // probe.
1140 enableMuxChips();
1141
1142 // Nothing needs to be done. Service restarted or BMC re-booted for
1143 // some reason at system power on.
1144 }
1145 catch (const std::exception& l_ex)
1146 {
1147 // Seeting of collection status should be utility method
1148 vpdSpecificUtility::setCollectionStatusProperty(
1149 SYSTEM_VPD_FILE_PATH, types::VpdCollectionStatus::Failed,
1150 m_sysCfgJsonObj, l_errCode);
1151
1152 if (l_errCode)
1153 {
1154 m_logger->logMessage(
1155 "Failed to set collection status for path " +
1156 std::string(SYSTEM_VPD_FILE_PATH) +
1157 "Reason: " + commonUtility::getErrCodeMsg(l_errCode));
1158 }
1159
1160 // Any issue in system's inital set up is handled in this catch. Error
1161 // will not propogate to manager.
1162 const types::PelInfoTuple l_pel(
1163 EventLogger::getErrorType(l_ex), types::SeverityType::Critical, 0,
1164 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
1165
1166 m_logger->logMessage(
1167 std::string("Exception while performing initial set up. ") +
1168 EventLogger::getErrorMsg(l_ex),
1169 PlaceHolder::PEL, &l_pel);
1170 }
1171 }
1172
collectAllFruVpd()1173 void IbmHandler::collectAllFruVpd()
1174 {
1175 // Setting status to "InProgress", before trigeering VPD collection.
1176 m_progressInterface->set_property(
1177 "Status", std::string(constants::vpdCollectionInProgress));
1178 m_worker->collectFrusFromJson();
1179 SetTimerToDetectVpdCollectionStatus();
1180 }
1181
isRbmcPrototypeSystem(uint16_t & o_errCode) const1182 bool IbmHandler::isRbmcPrototypeSystem(uint16_t& o_errCode) const noexcept
1183 {
1184 types::BinaryVector l_imValue = dbusUtility::getImFromDbus();
1185 if (l_imValue.empty())
1186 {
1187 o_errCode = error_code::DBUS_FAILURE;
1188 return false;
1189 }
1190
1191 if (constants::rbmcPrototypeSystemImValue == l_imValue)
1192 {
1193 return true;
1194 }
1195
1196 return false;
1197 }
1198
checkAndUpdateBmcPosition(size_t & o_bmcPosition) const1199 void IbmHandler::checkAndUpdateBmcPosition(size_t& o_bmcPosition) const noexcept
1200 {
1201 if (m_sysCfgJsonObj.empty())
1202 {
1203 m_logger->logMessage(
1204 "System config JSON is empty, unable to find BMC position");
1205 return;
1206 }
1207
1208 uint16_t l_errCode = 0;
1209 std::string l_motherboardEepromPath = jsonUtility::getFruPathFromJson(
1210 m_sysCfgJsonObj, constants::systemVpdInvPath, l_errCode);
1211
1212 if (!l_motherboardEepromPath.empty())
1213 {
1214 o_bmcPosition = constants::VALUE_1;
1215 std::error_code l_ec;
1216 if (std::filesystem::exists(l_motherboardEepromPath, l_ec))
1217 {
1218 o_bmcPosition = constants::VALUE_0;
1219 }
1220 }
1221 else if (l_errCode)
1222 {
1223 m_logger->logMessage("Unable to determine BMC position, reason: " +
1224 commonUtility::getErrCodeMsg(l_errCode));
1225 }
1226 else
1227 {
1228 m_logger->logMessage("Unable to determine BMC position, as FRU path[" +
1229 std::string(constants::systemVpdInvPath) +
1230 "], not found in the system config JSON.");
1231 }
1232 }
1233 } // namespace vpd
1234