1 #include "config.h" 2 3 #include "ibm_handler.hpp" 4 5 #include "listener.hpp" 6 #include "parser.hpp" 7 8 #include <utility/common_utility.hpp> 9 #include <utility/dbus_utility.hpp> 10 #include <utility/json_utility.hpp> 11 #include <utility/vpd_specific_utility.hpp> 12 13 namespace vpd 14 { 15 IbmHandler::IbmHandler( 16 std::shared_ptr<Worker>& o_worker, 17 std::shared_ptr<BackupAndRestore>& o_backupAndRestoreObj, 18 const std::shared_ptr<sdbusplus::asio::dbus_interface>& i_iFace, 19 const std::shared_ptr<sdbusplus::asio::dbus_interface>& i_progressiFace, 20 const std::shared_ptr<boost::asio::io_context>& i_ioCon, 21 const std::shared_ptr<sdbusplus::asio::connection>& i_asioConnection) : 22 m_worker(o_worker), m_backupAndRestoreObj(o_backupAndRestoreObj), 23 m_interface(i_iFace), m_progressInterface(i_progressiFace), 24 m_ioContext(i_ioCon), m_asioConnection(i_asioConnection), 25 m_logger(Logger::getLoggerInstance()) 26 { 27 uint16_t l_errCode{0}; 28 29 // check VPD collection mode 30 const auto l_vpdCollectionMode = 31 commonUtility::isFieldModeEnabled() 32 ? types::VpdCollectionMode::DEFAULT_MODE 33 : commonUtility::getVpdCollectionMode(l_errCode); 34 35 if (l_errCode) 36 { 37 m_logger->logMessage( 38 "Error while trying to read VPD collection mode: " + 39 commonUtility::getErrCodeMsg(l_errCode)); 40 } 41 42 if (dbusUtility::isChassisPowerOn()) 43 { 44 // At power on, less number of FRU(s) needs collection. we can scale 45 // down the threads to reduce CPU utilization. 46 m_worker = std::make_shared<Worker>( 47 INVENTORY_JSON_DEFAULT, constants::VALUE_1, l_vpdCollectionMode); 48 } 49 else 50 { 51 // Initialize with default configuration 52 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT, 53 constants::MAX_THREADS, 54 l_vpdCollectionMode); 55 } 56 57 // Set up minimal things that is needed before bus name is claimed. 58 performInitialSetup(); 59 60 if (!m_sysCfgJsonObj.empty() && 61 jsonUtility::isBackupAndRestoreRequired(m_sysCfgJsonObj, l_errCode)) 62 { 63 try 64 { 65 m_backupAndRestoreObj = 66 std::make_shared<BackupAndRestore>(m_sysCfgJsonObj); 67 } 68 catch (const std::exception& l_ex) 69 { 70 logging::logMessage("Back up and restore instantiation failed. {" + 71 std::string(l_ex.what()) + "}"); 72 73 EventLogger::createSyncPel( 74 EventLogger::getErrorType(l_ex), types::SeverityType::Warning, 75 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 76 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 77 } 78 } 79 else if (l_errCode) 80 { 81 logging::logMessage( 82 "Failed to check if backup & restore required. Error : " + 83 commonUtility::getErrCodeMsg(l_errCode)); 84 } 85 86 // Instantiate Listener object 87 m_eventListener = std::make_shared<Listener>(m_worker, m_asioConnection); 88 m_eventListener->registerAssetTagChangeCallback(); 89 m_eventListener->registerHostStateChangeCallback(); 90 m_eventListener->registerPresenceChangeCallback(); 91 92 // Instantiate GpioMonitor class 93 m_gpioMonitor = 94 std::make_shared<GpioMonitor>(m_sysCfgJsonObj, m_worker, m_ioContext); 95 } 96 97 void IbmHandler::SetTimerToDetectVpdCollectionStatus() 98 { 99 // Keeping max retry for 2 minutes. TODO: Make it configurable based on 100 // system type. 101 static constexpr auto MAX_RETRY = 12; 102 103 static boost::asio::steady_timer l_timer(*m_ioContext); 104 static uint8_t l_timerRetry = 0; 105 106 auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(10)); 107 108 (l_asyncCancelled == 0) 109 ? logging::logMessage("Collection Timer started") 110 : logging::logMessage("Collection Timer re-started"); 111 112 l_timer.async_wait([this](const boost::system::error_code& ec) { 113 if (ec == boost::asio::error::operation_aborted) 114 { 115 throw std::runtime_error( 116 "Timer to detect thread collection status was aborted"); 117 } 118 119 if (ec) 120 { 121 throw std::runtime_error( 122 "Timer to detect thread collection failed"); 123 } 124 125 if (m_worker->isAllFruCollectionDone()) 126 { 127 // cancel the timer 128 l_timer.cancel(); 129 processFailedEeproms(); 130 131 // update VPD for powerVS system. 132 ConfigurePowerVsSystem(); 133 134 std::cout << "m_worker->isSystemVPDOnDBus() completed" << std::endl; 135 m_progressInterface->set_property( 136 "Status", std::string(constants::vpdCollectionCompleted)); 137 138 if (m_backupAndRestoreObj) 139 { 140 m_backupAndRestoreObj->backupAndRestore(); 141 } 142 143 if (m_eventListener) 144 { 145 // Check if system config JSON specifies 146 // correlatedPropertiesJson 147 if (m_sysCfgJsonObj.contains("correlatedPropertiesConfigPath")) 148 { 149 // register correlated properties callback with specific 150 // correlated properties JSON 151 m_eventListener->registerCorrPropCallBack( 152 m_sysCfgJsonObj["correlatedPropertiesConfigPath"]); 153 } 154 else 155 { 156 m_logger->logMessage( 157 "Correlated properties JSON path is not defined in system config JSON. Correlated properties listener is disabled."); 158 } 159 } 160 161 // terminate collection logger 162 m_logger->terminateVpdCollectionLogging(); 163 } 164 else 165 { 166 auto l_threadCount = m_worker->getActiveThreadCount(); 167 if (l_timerRetry == MAX_RETRY) 168 { 169 l_timer.cancel(); 170 logging::logMessage("Taking too long. Active thread = " + 171 std::to_string(l_threadCount)); 172 173 // terminate collection logger 174 m_logger->terminateVpdCollectionLogging(); 175 } 176 else 177 { 178 l_timerRetry++; 179 logging::logMessage("Collection is in progress for [" + 180 std::to_string(l_threadCount) + "] FRUs."); 181 182 SetTimerToDetectVpdCollectionStatus(); 183 } 184 } 185 }); 186 } 187 188 void IbmHandler::checkAndUpdatePowerVsVpd( 189 const nlohmann::json& i_powerVsJsonObj, 190 std::vector<std::string>& o_failedPathList) 191 { 192 for (const auto& [l_fruPath, l_recJson] : i_powerVsJsonObj.items()) 193 { 194 nlohmann::json l_sysCfgJsonObj{}; 195 if (m_worker.get() != nullptr) 196 { 197 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 198 } 199 200 // The utility method will handle emty JSON case. No explicit 201 // handling required here. 202 uint16_t l_errCode = 0; 203 auto l_inventoryPath = jsonUtility::getInventoryObjPathFromJson( 204 l_sysCfgJsonObj, l_fruPath, l_errCode); 205 206 // Mark it as failed if inventory path not found in JSON. 207 if (l_inventoryPath.empty()) 208 { 209 if (l_errCode) 210 { 211 logging::logMessage( 212 "Failed to get inventory object path from JSON for FRU [" + 213 l_fruPath + 214 "], error : " + commonUtility::getErrCodeMsg(l_errCode)); 215 } 216 217 o_failedPathList.push_back(l_fruPath); 218 continue; 219 } 220 221 // check if the FRU is present 222 if (!dbusUtility::isInventoryPresent(l_inventoryPath)) 223 { 224 logging::logMessage( 225 "Inventory not present, skip updating part number. Path: " + 226 l_inventoryPath); 227 continue; 228 } 229 230 // check if the FRU needs CCIN check before updating PN. 231 if (l_recJson.contains("CCIN")) 232 { 233 const auto& l_ccinFromDbus = 234 vpdSpecificUtility::getCcinFromDbus(l_inventoryPath, l_errCode); 235 236 // Not an ideal situation as CCIN can't be empty. 237 if (l_ccinFromDbus.empty()) 238 { 239 if (l_errCode) 240 { 241 m_logger->logMessage( 242 "Failed to get CCIN value from DBus, error : " + 243 commonUtility::getErrCodeMsg(l_errCode)); 244 } 245 246 o_failedPathList.push_back(l_fruPath); 247 continue; 248 } 249 250 std::vector<std::string> l_ccinListFromJson = l_recJson["CCIN"]; 251 252 if (find(l_ccinListFromJson.begin(), l_ccinListFromJson.end(), 253 l_ccinFromDbus) == l_ccinListFromJson.end()) 254 { 255 // Don't update PN in this case. 256 continue; 257 } 258 } 259 260 for (const auto& [l_recordName, l_kwdJson] : l_recJson.items()) 261 { 262 // Record name can't be CCIN, skip processing as it is there for PN 263 // update based on CCIN check. 264 if (l_recordName == constants::kwdCCIN) 265 { 266 continue; 267 } 268 269 for (const auto& [l_kwdName, l_kwdValue] : l_kwdJson.items()) 270 { 271 // Is value of type array. 272 if (!l_kwdValue.is_array()) 273 { 274 o_failedPathList.push_back(l_fruPath); 275 continue; 276 } 277 278 // Get current FRU Part number. 279 auto l_retVal = dbusUtility::readDbusProperty( 280 constants::pimServiceName, l_inventoryPath, 281 constants::viniInf, constants::kwdFN); 282 283 auto l_ptrToFn = std::get_if<types::BinaryVector>(&l_retVal); 284 285 if (!l_ptrToFn) 286 { 287 o_failedPathList.push_back(l_fruPath); 288 continue; 289 } 290 291 types::BinaryVector l_binaryKwdValue = 292 l_kwdValue.get<types::BinaryVector>(); 293 if (l_binaryKwdValue == (*l_ptrToFn)) 294 { 295 continue; 296 } 297 298 // Update part number only if required. 299 std::shared_ptr<Parser> l_parserObj = 300 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj); 301 if (l_parserObj->updateVpdKeyword(std::make_tuple( 302 l_recordName, l_kwdName, l_binaryKwdValue)) == 303 constants::FAILURE) 304 { 305 o_failedPathList.push_back(l_fruPath); 306 continue; 307 } 308 309 // update the Asset interface Spare part number explicitly. 310 if (!dbusUtility::callPIM(types::ObjectMap{ 311 {l_inventoryPath, 312 {{constants::assetInf, 313 {{"SparePartNumber", 314 std::string(l_binaryKwdValue.begin(), 315 l_binaryKwdValue.end())}}}}}})) 316 { 317 logging::logMessage( 318 "Updating Spare Part Number under Asset interface failed for path [" + 319 l_inventoryPath + "]"); 320 } 321 322 // Just needed for logging. 323 std::string l_initialPartNum((*l_ptrToFn).begin(), 324 (*l_ptrToFn).end()); 325 std::string l_finalPartNum(l_binaryKwdValue.begin(), 326 l_binaryKwdValue.end()); 327 logging::logMessage( 328 "FRU Part number updated for path [" + l_inventoryPath + 329 "]" + "From [" + l_initialPartNum + "]" + " to [" + 330 l_finalPartNum + "]"); 331 } 332 } 333 } 334 } 335 336 void IbmHandler::ConfigurePowerVsSystem() 337 { 338 std::vector<std::string> l_failedPathList; 339 try 340 { 341 types::BinaryVector l_imValue = dbusUtility::getImFromDbus(); 342 if (l_imValue.empty()) 343 { 344 throw DbusException("Invalid IM value read from Dbus"); 345 } 346 347 uint16_t l_errCode = 0; 348 if (!vpdSpecificUtility::isPowerVsConfiguration(l_imValue, l_errCode)) 349 { 350 // TODO: Should booting be blocked in case of some 351 // misconfigurations? 352 if (l_errCode) 353 { 354 logging::logMessage( 355 "Failed to check if the system is powerVs Configuration, error : " + 356 commonUtility::getErrCodeMsg(l_errCode)); 357 } 358 359 return; 360 } 361 362 const nlohmann::json& l_powerVsJsonObj = 363 jsonUtility::getPowerVsJson(l_imValue, l_errCode); 364 365 if (l_powerVsJsonObj.empty()) 366 { 367 throw std::runtime_error("PowerVS Json not found. Error : " + 368 commonUtility::getErrCodeMsg(l_errCode)); 369 } 370 371 checkAndUpdatePowerVsVpd(l_powerVsJsonObj, l_failedPathList); 372 373 if (!l_failedPathList.empty()) 374 { 375 throw std::runtime_error( 376 "Part number update failed for following paths: "); 377 } 378 } 379 catch (const std::exception& l_ex) 380 { 381 // TODO log appropriate PEL 382 } 383 } 384 385 void IbmHandler::processFailedEeproms() 386 { 387 if (m_worker.get() != nullptr) 388 { 389 // TODO: 390 // - iterate through list of EEPROMs for which thread creation has 391 // failed 392 // - For each failed EEPROM, trigger VPD collection 393 m_worker->getFailedEepromPaths().clear(); 394 } 395 } 396 397 void IbmHandler::enableMuxChips() 398 { 399 if (m_sysCfgJsonObj.empty()) 400 { 401 // config JSON should not be empty at this point of execution. 402 throw std::runtime_error("Config JSON is empty. Can't enable muxes"); 403 return; 404 } 405 406 if (!m_sysCfgJsonObj.contains("muxes")) 407 { 408 logging::logMessage("No mux defined for the system in config JSON"); 409 return; 410 } 411 412 // iterate over each MUX detail and enable them. 413 for (const auto& item : m_sysCfgJsonObj["muxes"]) 414 { 415 if (item.contains("holdidlepath")) 416 { 417 std::string cmd = "echo 0 > "; 418 cmd += item["holdidlepath"]; 419 420 logging::logMessage("Enabling mux with command = " + cmd); 421 422 commonUtility::executeCmd(cmd); 423 continue; 424 } 425 426 logging::logMessage( 427 "Mux Entry does not have hold idle path. Can't enable the mux"); 428 } 429 } 430 431 void IbmHandler::performInitialSetup() 432 { 433 try 434 { 435 if (m_worker.get() == nullptr) 436 { 437 throw std::runtime_error( 438 "Worker object not found. Can't perform initial setup."); 439 } 440 441 m_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 442 if (!dbusUtility::isChassisPowerOn()) 443 { 444 m_worker->setDeviceTreeAndJson(); 445 446 // Since the above function setDeviceTreeAndJson can change the json 447 // which is used, we would need to reacquire the json object again 448 // here. 449 m_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 450 } 451 452 // Update BMC postion for RBMC prototype system 453 // Ignore BMC position update in case of any error 454 uint16_t l_errCode = 0; 455 if (isRbmcPrototypeSystem(l_errCode)) 456 { 457 size_t l_bmcPosition = std::numeric_limits<size_t>::max(); 458 checkAndUpdateBmcPosition(l_bmcPosition); 459 460 if (dbusUtility::callPIM(types::ObjectMap{ 461 {sdbusplus::message::object_path(constants::systemInvPath), 462 {{constants::rbmcPositionInterface, 463 {{"Position", l_bmcPosition}}}}}})) 464 { 465 m_logger->logMessage( 466 "Updating BMC position failed for path [" + 467 std::string(constants::systemInvPath) + 468 "], bmc position: " + std::to_string(l_bmcPosition)); 469 470 // ToDo: Check is PEL required 471 } 472 } 473 else if (l_errCode != 0) 474 { 475 m_logger->logMessage( 476 "Unable to determine whether system is RBMC system or not, reason: " + 477 commonUtility::getErrCodeMsg(l_errCode)); 478 } 479 480 // Enable all mux which are used for connecting to the i2c on the 481 // pcie slots for pcie cards. These are not enabled by kernel due to 482 // an issue seen with Castello cards, where the i2c line hangs on a 483 // probe. 484 enableMuxChips(); 485 486 // Nothing needs to be done. Service restarted or BMC re-booted for 487 // some reason at system power on. 488 } 489 catch (const std::exception& l_ex) 490 { 491 m_worker->setCollectionStatusProperty(SYSTEM_VPD_FILE_PATH, 492 constants::vpdCollectionFailed); 493 // Any issue in system's inital set up is handled in this catch. Error 494 // will not propogate to manager. 495 EventLogger::createSyncPel( 496 EventLogger::getErrorType(l_ex), types::SeverityType::Critical, 497 __FILE__, __FUNCTION__, 0, EventLogger::getErrorMsg(l_ex), 498 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 499 } 500 } 501 502 void IbmHandler::collectAllFruVpd() 503 { 504 // initialize VPD collection logger 505 m_logger->initiateVpdCollectionLogging(); 506 507 // Setting status to "InProgress", before trigeering VPD collection. 508 m_progressInterface->set_property( 509 "Status", std::string(constants::vpdCollectionInProgress)); 510 m_worker->collectFrusFromJson(); 511 SetTimerToDetectVpdCollectionStatus(); 512 } 513 514 bool IbmHandler::isRbmcPrototypeSystem(uint16_t& o_errCode) const noexcept 515 { 516 types::BinaryVector l_imValue = dbusUtility::getImFromDbus(); 517 if (l_imValue.empty()) 518 { 519 o_errCode = error_code::DBUS_FAILURE; 520 return false; 521 } 522 523 if (constants::rbmcPrototypeSystemImValue == l_imValue) 524 { 525 return true; 526 } 527 528 return false; 529 } 530 531 void IbmHandler::checkAndUpdateBmcPosition(size_t& o_bmcPosition) const noexcept 532 { 533 if (m_worker.get() == nullptr) 534 { 535 m_logger->logMessage("Worker object not found"); 536 return; 537 } 538 539 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 540 if (l_sysCfgJsonObj.empty()) 541 { 542 m_logger->logMessage( 543 "System config JSON is empty, unable to find BMC position"); 544 return; 545 } 546 547 uint16_t l_errCode = 0; 548 std::string l_motherboardEepromPath = jsonUtility::getFruPathFromJson( 549 l_sysCfgJsonObj, constants::systemVpdInvPath, l_errCode); 550 551 if (!l_motherboardEepromPath.empty()) 552 { 553 o_bmcPosition = constants::VALUE_1; 554 std::error_code l_ec; 555 if (std::filesystem::exists(l_motherboardEepromPath, l_ec)) 556 { 557 o_bmcPosition = constants::VALUE_0; 558 } 559 } 560 else if (l_errCode) 561 { 562 m_logger->logMessage("Unable to determine BMC position, reason: " + 563 commonUtility::getErrCodeMsg(l_errCode)); 564 } 565 else 566 { 567 m_logger->logMessage("Unable to determine BMC position, as FRU path[" + 568 std::string(constants::systemVpdInvPath) + 569 "], not found in the system config JSON."); 570 } 571 } 572 } // namespace vpd 573