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