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