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