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