xref: /openbmc/openpower-vpd-parser/vpd-manager/oem-handler/ibm_handler.cpp (revision b0e7d59b2016d17400c7fccdbf3268dd2e505775)
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