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