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