1 #include "config.h" 2 3 #include "manager.hpp" 4 5 #include "constants.hpp" 6 #include "exceptions.hpp" 7 #include "logger.hpp" 8 #include "parser.hpp" 9 #include "parser_factory.hpp" 10 #include "parser_interface.hpp" 11 #include "single_fab.hpp" 12 #include "types.hpp" 13 #include "utility/dbus_utility.hpp" 14 #include "utility/json_utility.hpp" 15 #include "utility/vpd_specific_utility.hpp" 16 17 #include <boost/asio/steady_timer.hpp> 18 #include <sdbusplus/bus/match.hpp> 19 #include <sdbusplus/message.hpp> 20 21 namespace vpd 22 { 23 Manager::Manager( 24 const std::shared_ptr<boost::asio::io_context>& ioCon, 25 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace, 26 const std::shared_ptr<sdbusplus::asio::connection>& asioConnection) : 27 m_ioContext(ioCon), m_interface(iFace), m_asioConnection(asioConnection) 28 { 29 #ifdef IBM_SYSTEM 30 if (!dbusUtility::isChassisPowerOn()) 31 { 32 SingleFab l_singleFab; 33 const int& l_rc = l_singleFab.singleFabImOverride(); 34 35 if (l_rc == constants::FAILURE) 36 { 37 throw std::runtime_error( 38 std::string(__FUNCTION__) + 39 " : Found an invalid system configuration. Needs manual intervention. BMC is being quiesced."); 40 } 41 } 42 #endif 43 44 try 45 { 46 // For backward compatibility. Should be depricated. 47 iFace->register_method( 48 "WriteKeyword", 49 [this](const sdbusplus::message::object_path i_path, 50 const std::string i_recordName, const std::string i_keyword, 51 const types::BinaryVector i_value) -> int { 52 return this->updateKeyword( 53 i_path, std::make_tuple(i_recordName, i_keyword, i_value)); 54 }); 55 56 // Register methods under com.ibm.VPD.Manager interface 57 iFace->register_method( 58 "UpdateKeyword", 59 [this](const types::Path i_vpdPath, 60 const types::WriteVpdParams i_paramsToWriteData) -> int { 61 return this->updateKeyword(i_vpdPath, i_paramsToWriteData); 62 }); 63 64 iFace->register_method( 65 "WriteKeywordOnHardware", 66 [this](const types::Path i_fruPath, 67 const types::WriteVpdParams i_paramsToWriteData) -> int { 68 return this->updateKeywordOnHardware(i_fruPath, 69 i_paramsToWriteData); 70 }); 71 72 iFace->register_method( 73 "ReadKeyword", 74 [this](const types::Path i_fruPath, 75 const types::ReadVpdParams i_paramsToReadData) 76 -> types::DbusVariantType { 77 return this->readKeyword(i_fruPath, i_paramsToReadData); 78 }); 79 80 iFace->register_method( 81 "CollectFRUVPD", 82 [this](const sdbusplus::message::object_path& i_dbusObjPath) { 83 this->collectSingleFruVpd(i_dbusObjPath); 84 }); 85 86 iFace->register_method( 87 "deleteFRUVPD", 88 [this](const sdbusplus::message::object_path& i_dbusObjPath) { 89 this->deleteSingleFruVpd(i_dbusObjPath); 90 }); 91 92 iFace->register_method( 93 "GetExpandedLocationCode", 94 [this](const std::string& i_unexpandedLocationCode, 95 uint16_t& i_nodeNumber) -> std::string { 96 return this->getExpandedLocationCode(i_unexpandedLocationCode, 97 i_nodeNumber); 98 }); 99 100 iFace->register_method("GetFRUsByExpandedLocationCode", 101 [this](const std::string& i_expandedLocationCode) 102 -> types::ListOfPaths { 103 return this->getFrusByExpandedLocationCode( 104 i_expandedLocationCode); 105 }); 106 107 iFace->register_method( 108 "GetFRUsByUnexpandedLocationCode", 109 [this](const std::string& i_unexpandedLocationCode, 110 uint16_t& i_nodeNumber) -> types::ListOfPaths { 111 return this->getFrusByUnexpandedLocationCode( 112 i_unexpandedLocationCode, i_nodeNumber); 113 }); 114 115 iFace->register_method( 116 "GetHardwarePath", 117 [this](const sdbusplus::message::object_path& i_dbusObjPath) 118 -> std::string { return this->getHwPath(i_dbusObjPath); }); 119 120 iFace->register_method("PerformVPDRecollection", [this]() { 121 this->performVpdRecollection(); 122 }); 123 124 // Indicates FRU VPD collection for the system has not started. 125 iFace->register_property_rw<std::string>( 126 "CollectionStatus", sdbusplus::vtable::property_::emits_change, 127 [this](const std::string l_currStatus, const auto&) { 128 m_vpdCollectionStatus = l_currStatus; 129 return 0; 130 }, 131 [this](const auto&) { return m_vpdCollectionStatus; }); 132 133 // If required, instantiate OEM specific handler here. 134 #ifdef IBM_SYSTEM 135 m_ibmHandler = std::make_shared<IbmHandler>( 136 m_worker, m_backupAndRestoreObj, m_interface, m_ioContext, 137 m_asioConnection); 138 #else 139 m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT); 140 m_interface->set_property("CollectionStatus", std::string("Completed")); 141 #endif 142 } 143 catch (const std::exception& e) 144 { 145 logging::logMessage( 146 "Manager class instantiation failed. " + std::string(e.what())); 147 148 vpd::EventLogger::createSyncPel( 149 vpd::EventLogger::getErrorType(e), vpd::types::SeverityType::Error, 150 __FILE__, __FUNCTION__, 0, vpd::EventLogger::getErrorMsg(e), 151 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 152 } 153 } 154 155 int Manager::updateKeyword(const types::Path i_vpdPath, 156 const types::WriteVpdParams i_paramsToWriteData) 157 { 158 if (i_vpdPath.empty()) 159 { 160 logging::logMessage("Given VPD path is empty."); 161 return -1; 162 } 163 164 types::Path l_fruPath; 165 nlohmann::json l_sysCfgJsonObj{}; 166 167 if (m_worker.get() != nullptr) 168 { 169 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 170 171 // Get the EEPROM path 172 if (!l_sysCfgJsonObj.empty()) 173 { 174 l_fruPath = 175 jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath); 176 } 177 } 178 179 if (l_fruPath.empty()) 180 { 181 l_fruPath = i_vpdPath; 182 } 183 184 try 185 { 186 std::shared_ptr<Parser> l_parserObj = 187 std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj); 188 auto l_rc = l_parserObj->updateVpdKeyword(i_paramsToWriteData); 189 190 if (l_rc != constants::FAILURE && m_backupAndRestoreObj) 191 { 192 if (m_backupAndRestoreObj->updateKeywordOnPrimaryOrBackupPath( 193 l_fruPath, i_paramsToWriteData) < constants::VALUE_0) 194 { 195 logging::logMessage( 196 "Write success, but backup and restore failed for file[" + 197 l_fruPath + "]"); 198 } 199 } 200 201 // update keyword in inherited FRUs 202 if (l_rc != constants::FAILURE) 203 { 204 vpdSpecificUtility::updateKwdOnInheritedFrus( 205 l_fruPath, i_paramsToWriteData, l_sysCfgJsonObj); 206 } 207 208 // update common interface(s) properties 209 if (l_rc != constants::FAILURE) 210 { 211 vpdSpecificUtility::updateCiPropertyOfInheritedFrus( 212 l_fruPath, i_paramsToWriteData, l_sysCfgJsonObj); 213 } 214 215 return l_rc; 216 } 217 catch (const std::exception& l_exception) 218 { 219 // TODO:: error log needed 220 logging::logMessage("Update keyword failed for file[" + i_vpdPath + 221 "], reason: " + std::string(l_exception.what())); 222 return -1; 223 } 224 } 225 226 int Manager::updateKeywordOnHardware( 227 const types::Path i_fruPath, 228 const types::WriteVpdParams i_paramsToWriteData) noexcept 229 { 230 try 231 { 232 if (i_fruPath.empty()) 233 { 234 throw std::runtime_error("Given FRU path is empty"); 235 } 236 237 nlohmann::json l_sysCfgJsonObj{}; 238 239 if (m_worker.get() != nullptr) 240 { 241 l_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 242 } 243 244 std::shared_ptr<Parser> l_parserObj = 245 std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj); 246 return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData); 247 } 248 catch (const std::exception& l_exception) 249 { 250 EventLogger::createAsyncPel( 251 types::ErrorType::InvalidEeprom, types::SeverityType::Informational, 252 __FILE__, __FUNCTION__, 0, 253 "Update keyword on hardware failed for file[" + i_fruPath + 254 "], reason: " + std::string(l_exception.what()), 255 std::nullopt, std::nullopt, std::nullopt, std::nullopt); 256 257 return constants::FAILURE; 258 } 259 } 260 261 types::DbusVariantType Manager::readKeyword( 262 const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData) 263 { 264 try 265 { 266 nlohmann::json l_jsonObj{}; 267 268 if (m_worker.get() != nullptr) 269 { 270 l_jsonObj = m_worker->getSysCfgJsonObj(); 271 } 272 273 std::error_code ec; 274 275 // Check if given path is filesystem path 276 if (!std::filesystem::exists(i_fruPath, ec) && (ec)) 277 { 278 throw std::runtime_error( 279 "Given file path " + i_fruPath + " not found."); 280 } 281 282 logging::logMessage("Performing VPD read on " + i_fruPath); 283 284 std::shared_ptr<vpd::Parser> l_parserObj = 285 std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj); 286 287 std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance = 288 l_parserObj->getVpdParserInstance(); 289 290 return ( 291 l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData)); 292 } 293 catch (const std::exception& e) 294 { 295 logging::logMessage( 296 e.what() + std::string(". VPD manager read operation failed for ") + 297 i_fruPath); 298 throw types::DeviceError::ReadFailure(); 299 } 300 } 301 302 void Manager::collectSingleFruVpd( 303 const sdbusplus::message::object_path& i_dbusObjPath) 304 { 305 if (m_vpdCollectionStatus != "Completed") 306 { 307 logging::logMessage( 308 "Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " + 309 std::string(i_dbusObjPath)); 310 return; 311 } 312 313 if (m_worker.get() != nullptr) 314 { 315 m_worker->collectSingleFruVpd(i_dbusObjPath); 316 } 317 } 318 319 void Manager::deleteSingleFruVpd( 320 const sdbusplus::message::object_path& i_dbusObjPath) 321 { 322 try 323 { 324 if (std::string(i_dbusObjPath).empty()) 325 { 326 throw std::runtime_error( 327 "Given DBus object path is empty. Aborting FRU VPD deletion."); 328 } 329 330 if (m_worker.get() == nullptr) 331 { 332 throw std::runtime_error( 333 "Worker object not found, can't perform FRU VPD deletion for: " + 334 std::string(i_dbusObjPath)); 335 } 336 337 m_worker->deleteFruVpd(std::string(i_dbusObjPath)); 338 } 339 catch (const std::exception& l_ex) 340 { 341 // TODO: Log PEL 342 logging::logMessage(l_ex.what()); 343 } 344 } 345 346 bool Manager::isValidUnexpandedLocationCode( 347 const std::string& i_unexpandedLocationCode) 348 { 349 if ((i_unexpandedLocationCode.length() < 350 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) || 351 ((i_unexpandedLocationCode.compare(0, 4, "Ufcs") != 352 constants::STR_CMP_SUCCESS) && 353 (i_unexpandedLocationCode.compare(0, 4, "Umts") != 354 constants::STR_CMP_SUCCESS)) || 355 ((i_unexpandedLocationCode.length() > 356 constants::UNEXP_LOCATION_CODE_MIN_LENGTH) && 357 (i_unexpandedLocationCode.find("-") != 4))) 358 { 359 return false; 360 } 361 362 return true; 363 } 364 365 std::string Manager::getExpandedLocationCode( 366 const std::string& i_unexpandedLocationCode, 367 [[maybe_unused]] const uint16_t i_nodeNumber) 368 { 369 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode)) 370 { 371 phosphor::logging::elog<types::DbusInvalidArgument>( 372 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 373 types::InvalidArgument::ARGUMENT_VALUE( 374 i_unexpandedLocationCode.c_str())); 375 } 376 377 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 378 if (!l_sysCfgJsonObj.contains("frus")) 379 { 380 logging::logMessage("Missing frus tag in system config JSON"); 381 } 382 383 const nlohmann::json& l_listOfFrus = 384 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>(); 385 386 for (const auto& l_frus : l_listOfFrus.items()) 387 { 388 for (const auto& l_aFru : l_frus.value()) 389 { 390 if (l_aFru["extraInterfaces"].contains( 391 constants::locationCodeInf) && 392 l_aFru["extraInterfaces"][constants::locationCodeInf].value( 393 "LocationCode", "") == i_unexpandedLocationCode) 394 { 395 return std::get<std::string>(dbusUtility::readDbusProperty( 396 l_aFru["serviceName"], l_aFru["inventoryPath"], 397 constants::locationCodeInf, "LocationCode")); 398 } 399 } 400 } 401 phosphor::logging::elog<types::DbusInvalidArgument>( 402 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 403 types::InvalidArgument::ARGUMENT_VALUE( 404 i_unexpandedLocationCode.c_str())); 405 } 406 407 types::ListOfPaths Manager::getFrusByUnexpandedLocationCode( 408 const std::string& i_unexpandedLocationCode, 409 [[maybe_unused]] const uint16_t i_nodeNumber) 410 { 411 types::ListOfPaths l_inventoryPaths; 412 413 if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode)) 414 { 415 phosphor::logging::elog<types::DbusInvalidArgument>( 416 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 417 types::InvalidArgument::ARGUMENT_VALUE( 418 i_unexpandedLocationCode.c_str())); 419 } 420 421 const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj(); 422 if (!l_sysCfgJsonObj.contains("frus")) 423 { 424 logging::logMessage("Missing frus tag in system config JSON"); 425 } 426 427 const nlohmann::json& l_listOfFrus = 428 l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>(); 429 430 for (const auto& l_frus : l_listOfFrus.items()) 431 { 432 for (const auto& l_aFru : l_frus.value()) 433 { 434 if (l_aFru["extraInterfaces"].contains( 435 constants::locationCodeInf) && 436 l_aFru["extraInterfaces"][constants::locationCodeInf].value( 437 "LocationCode", "") == i_unexpandedLocationCode) 438 { 439 l_inventoryPaths.push_back( 440 l_aFru.at("inventoryPath") 441 .get_ref<const nlohmann::json::string_t&>()); 442 } 443 } 444 } 445 446 if (l_inventoryPaths.empty()) 447 { 448 phosphor::logging::elog<types::DbusInvalidArgument>( 449 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 450 types::InvalidArgument::ARGUMENT_VALUE( 451 i_unexpandedLocationCode.c_str())); 452 } 453 454 return l_inventoryPaths; 455 } 456 457 std::string Manager::getHwPath( 458 const sdbusplus::message::object_path& i_dbusObjPath) 459 { 460 // Dummy code to supress unused variable warning. To be removed. 461 logging::logMessage(std::string(i_dbusObjPath)); 462 463 return std::string{}; 464 } 465 466 std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode( 467 const std::string& i_expandedLocationCode) 468 { 469 /** 470 * Location code should always start with U and fulfil minimum length 471 * criteria. 472 */ 473 if (i_expandedLocationCode[0] != 'U' || 474 i_expandedLocationCode.length() < 475 constants::EXP_LOCATION_CODE_MIN_LENGTH) 476 { 477 phosphor::logging::elog<types::DbusInvalidArgument>( 478 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 479 types::InvalidArgument::ARGUMENT_VALUE( 480 i_expandedLocationCode.c_str())); 481 } 482 483 std::string l_fcKwd; 484 485 auto l_fcKwdValue = dbusUtility::readDbusProperty( 486 "xyz.openbmc_project.Inventory.Manager", 487 "/xyz/openbmc_project/inventory/system/chassis/motherboard", 488 "com.ibm.ipzvpd.VCEN", "FC"); 489 490 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue)) 491 { 492 l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end()); 493 } 494 495 // Get the first part of expanded location code to check for FC or TM. 496 std::string l_firstKwd = i_expandedLocationCode.substr(1, 4); 497 498 std::string l_unexpandedLocationCode{}; 499 uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER; 500 501 // Check if this value matches the value of FC keyword. 502 if (l_fcKwd.substr(0, 4) == l_firstKwd) 503 { 504 /** 505 * Period(.) should be there in expanded location code to seggregate 506 * FC, node number and SE values. 507 */ 508 size_t l_nodeStartPos = i_expandedLocationCode.find('.'); 509 if (l_nodeStartPos == std::string::npos) 510 { 511 phosphor::logging::elog<types::DbusInvalidArgument>( 512 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 513 types::InvalidArgument::ARGUMENT_VALUE( 514 i_expandedLocationCode.c_str())); 515 } 516 517 size_t l_nodeEndPos = 518 i_expandedLocationCode.find('.', l_nodeStartPos + 1); 519 if (l_nodeEndPos == std::string::npos) 520 { 521 phosphor::logging::elog<types::DbusInvalidArgument>( 522 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 523 types::InvalidArgument::ARGUMENT_VALUE( 524 i_expandedLocationCode.c_str())); 525 } 526 527 // Skip 3 bytes for '.ND' 528 l_nodeNummber = std::stoi(i_expandedLocationCode.substr( 529 l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3))); 530 531 /** 532 * Confirm if there are other details apart FC, node number and SE 533 * in location code 534 */ 535 if (i_expandedLocationCode.length() > 536 constants::EXP_LOCATION_CODE_MIN_LENGTH) 537 { 538 l_unexpandedLocationCode = 539 i_expandedLocationCode[0] + std::string("fcs") + 540 i_expandedLocationCode.substr( 541 l_nodeEndPos + 1 + constants::SE_KWD_LENGTH, 542 std::string::npos); 543 } 544 else 545 { 546 l_unexpandedLocationCode = "Ufcs"; 547 } 548 } 549 else 550 { 551 std::string l_tmKwd; 552 // Read TM keyword value. 553 auto l_tmKwdValue = dbusUtility::readDbusProperty( 554 "xyz.openbmc_project.Inventory.Manager", 555 "/xyz/openbmc_project/inventory/system/chassis/motherboard", 556 "com.ibm.ipzvpd.VSYS", "TM"); 557 558 if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue)) 559 { 560 l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end()); 561 } 562 563 // Check if the substr matches to TM keyword value. 564 if (l_tmKwd.substr(0, 4) == l_firstKwd) 565 { 566 /** 567 * System location code will not have node number and any other 568 * details. 569 */ 570 l_unexpandedLocationCode = "Umts"; 571 } 572 // The given location code is neither "fcs" or "mts". 573 else 574 { 575 phosphor::logging::elog<types::DbusInvalidArgument>( 576 types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"), 577 types::InvalidArgument::ARGUMENT_VALUE( 578 i_expandedLocationCode.c_str())); 579 } 580 } 581 582 return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber); 583 } 584 585 types::ListOfPaths Manager::getFrusByExpandedLocationCode( 586 const std::string& i_expandedLocationCode) 587 { 588 std::tuple<std::string, uint16_t> l_locationAndNodePair = 589 getUnexpandedLocationCode(i_expandedLocationCode); 590 591 return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair), 592 std::get<1>(l_locationAndNodePair)); 593 } 594 595 void Manager::performVpdRecollection() 596 { 597 if (m_worker.get() != nullptr) 598 { 599 m_worker->performVpdRecollection(); 600 } 601 } 602 } // namespace vpd 603