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