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