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