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