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