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