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