1b0ce996aSGunnar Mills #include "config.h" 2b0ce996aSGunnar Mills 3b0ce996aSGunnar Mills #include "item_updater.hpp" 4b0ce996aSGunnar Mills 5b0ce996aSGunnar Mills #include "images.hpp" 6b0ce996aSGunnar Mills #include "serialize.hpp" 7b0ce996aSGunnar Mills #include "version.hpp" 8b0ce996aSGunnar Mills #include "xyz/openbmc_project/Software/Version/server.hpp" 9b0ce996aSGunnar Mills 10c98d912eSAdriana Kobylak #include <filesystem> 1135e83f3eSSaqib Khan #include <fstream> 12d5b8f75cSAdriana Kobylak #include <phosphor-logging/elog-errors.hpp> 13b0ce996aSGunnar Mills #include <phosphor-logging/elog.hpp> 14b0ce996aSGunnar Mills #include <phosphor-logging/log.hpp> 15204e1e74SAdriana Kobylak #include <queue> 16b77551cdSAdriana Kobylak #include <set> 17ec1b41c4SGunnar Mills #include <string> 18*60f5ccfdSGunnar Mills #include <thread> 19d5b8f75cSAdriana Kobylak #include <xyz/openbmc_project/Common/error.hpp> 2043699ca7SAdriana Kobylak #include <xyz/openbmc_project/Software/Image/error.hpp> 21ec1b41c4SGunnar Mills 22ec1b41c4SGunnar Mills namespace phosphor 23ec1b41c4SGunnar Mills { 24ec1b41c4SGunnar Mills namespace software 25ec1b41c4SGunnar Mills { 26ec1b41c4SGunnar Mills namespace updater 27ec1b41c4SGunnar Mills { 28ec1b41c4SGunnar Mills 292ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class 302ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server; 310129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server; 322ce7da29SGunnar Mills 332ce7da29SGunnar Mills using namespace phosphor::logging; 3443699ca7SAdriana Kobylak using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error; 352ab9b109SJayanth Othayoth using namespace phosphor::software::image; 36c98d912eSAdriana Kobylak namespace fs = std::filesystem; 37d5b8f75cSAdriana Kobylak using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 3835e83f3eSSaqib Khan 39e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg) 40ec1b41c4SGunnar Mills { 4184a0e693SSaqib Khan 4284a0e693SSaqib Khan using SVersion = server::Version; 4384a0e693SSaqib Khan using VersionPurpose = SVersion::VersionPurpose; 449a782243SGunnar Mills using VersionClass = phosphor::software::manager::Version; 4584a0e693SSaqib Khan namespace mesg = sdbusplus::message; 4684a0e693SSaqib Khan 4784a0e693SSaqib Khan mesg::object_path objPath; 4884a0e693SSaqib Khan auto purpose = VersionPurpose::Unknown; 49705f1bfcSSaqib Khan std::string version; 502285fe0fSAdriana Kobylak std::map<std::string, std::map<std::string, mesg::variant<std::string>>> 512285fe0fSAdriana Kobylak interfaces; 52e75d10f5SPatrick Williams msg.read(objPath, interfaces); 532ce7da29SGunnar Mills std::string path(std::move(objPath)); 5419177d3eSSaqib Khan std::string filePath; 552ce7da29SGunnar Mills 562ce7da29SGunnar Mills for (const auto& intf : interfaces) 572ce7da29SGunnar Mills { 58705f1bfcSSaqib Khan if (intf.first == VERSION_IFACE) 592ce7da29SGunnar Mills { 602ce7da29SGunnar Mills for (const auto& property : intf.second) 612ce7da29SGunnar Mills { 62705f1bfcSSaqib Khan if (property.first == "Purpose") 632ce7da29SGunnar Mills { 6484a0e693SSaqib Khan auto value = SVersion::convertVersionPurposeFromString( 65e883fb8bSPatrick Williams std::get<std::string>(property.second)); 6684a0e693SSaqib Khan if (value == VersionPurpose::BMC || 67e9f6c845SVijay Khemka #ifdef HOST_BIOS_UPGRADE 68e9f6c845SVijay Khemka value == VersionPurpose::Host || 69e9f6c845SVijay Khemka #endif 7084a0e693SSaqib Khan value == VersionPurpose::System) 7184a0e693SSaqib Khan { 7284a0e693SSaqib Khan purpose = value; 7384a0e693SSaqib Khan } 74705f1bfcSSaqib Khan } 75705f1bfcSSaqib Khan else if (property.first == "Version") 76705f1bfcSSaqib Khan { 77e883fb8bSPatrick Williams version = std::get<std::string>(property.second); 78705f1bfcSSaqib Khan } 79705f1bfcSSaqib Khan } 80705f1bfcSSaqib Khan } 8119177d3eSSaqib Khan else if (intf.first == FILEPATH_IFACE) 8219177d3eSSaqib Khan { 8319177d3eSSaqib Khan for (const auto& property : intf.second) 8419177d3eSSaqib Khan { 8519177d3eSSaqib Khan if (property.first == "Path") 8619177d3eSSaqib Khan { 87e883fb8bSPatrick Williams filePath = std::get<std::string>(property.second); 8819177d3eSSaqib Khan } 8919177d3eSSaqib Khan } 9019177d3eSSaqib Khan } 91705f1bfcSSaqib Khan } 922285fe0fSAdriana Kobylak if (version.empty() || filePath.empty() || 9384a0e693SSaqib Khan purpose == VersionPurpose::Unknown) 942ce7da29SGunnar Mills { 95e75d10f5SPatrick Williams return; 962ce7da29SGunnar Mills } 972ce7da29SGunnar Mills 982ce7da29SGunnar Mills // Version id is the last item in the path 992ce7da29SGunnar Mills auto pos = path.rfind("/"); 1002ce7da29SGunnar Mills if (pos == std::string::npos) 1012ce7da29SGunnar Mills { 1022ce7da29SGunnar Mills log<level::ERR>("No version id found in object path", 103596466b8SAdriana Kobylak entry("OBJPATH=%s", path.c_str())); 104e75d10f5SPatrick Williams return; 1052ce7da29SGunnar Mills } 1062ce7da29SGunnar Mills 1072ce7da29SGunnar Mills auto versionId = path.substr(pos + 1); 1082ce7da29SGunnar Mills 109e75d10f5SPatrick Williams if (activations.find(versionId) == activations.end()) 1102ce7da29SGunnar Mills { 11135e83f3eSSaqib Khan // Determine the Activation state by processing the given image dir. 11235e83f3eSSaqib Khan auto activationState = server::Activation::Activations::Invalid; 113e9f6c845SVijay Khemka ItemUpdater::ActivationStatus result; 114e9f6c845SVijay Khemka if (purpose == VersionPurpose::BMC || purpose == VersionPurpose::System) 115e9f6c845SVijay Khemka result = ItemUpdater::validateSquashFSImage(filePath); 116e9f6c845SVijay Khemka else 117e9f6c845SVijay Khemka result = ItemUpdater::ActivationStatus::ready; 118e9f6c845SVijay Khemka 11943b25cdeSGunnar Mills AssociationList associations = {}; 12043b25cdeSGunnar Mills 12135e83f3eSSaqib Khan if (result == ItemUpdater::ActivationStatus::ready) 12235e83f3eSSaqib Khan { 12335e83f3eSSaqib Khan activationState = server::Activation::Activations::Ready; 124b60add1eSGunnar Mills // Create an association to the BMC inventory item 1252285fe0fSAdriana Kobylak associations.emplace_back( 1262285fe0fSAdriana Kobylak std::make_tuple(ACTIVATION_FWD_ASSOCIATION, 1272285fe0fSAdriana Kobylak ACTIVATION_REV_ASSOCIATION, bmcInventoryPath)); 12843b25cdeSGunnar Mills } 129b60add1eSGunnar Mills 130ee13e831SSaqib Khan activations.insert(std::make_pair( 131ee13e831SSaqib Khan versionId, 1322285fe0fSAdriana Kobylak std::make_unique<Activation>(bus, path, *this, versionId, 1332285fe0fSAdriana Kobylak activationState, associations))); 1344254beceSMichael Tritz 135ee13e831SSaqib Khan auto versionPtr = std::make_unique<VersionClass>( 1362285fe0fSAdriana Kobylak bus, path, version, purpose, filePath, 1372285fe0fSAdriana Kobylak std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); 138ee13e831SSaqib Khan versionPtr->deleteObject = 1392285fe0fSAdriana Kobylak std::make_unique<phosphor::software::manager::Delete>(bus, path, 1402285fe0fSAdriana Kobylak *versionPtr); 141ee13e831SSaqib Khan versions.insert(std::make_pair(versionId, std::move(versionPtr))); 1422ce7da29SGunnar Mills } 143e75d10f5SPatrick Williams return; 144ec1b41c4SGunnar Mills } 145ec1b41c4SGunnar Mills 146ba239881SSaqib Khan void ItemUpdater::processBMCImage() 147ba239881SSaqib Khan { 14888e8a325SGunnar Mills using VersionClass = phosphor::software::manager::Version; 149269bff30SLei YU 150269bff30SLei YU // Check MEDIA_DIR and create if it does not exist 151269bff30SLei YU try 152269bff30SLei YU { 153269bff30SLei YU if (!fs::is_directory(MEDIA_DIR)) 154269bff30SLei YU { 155269bff30SLei YU fs::create_directory(MEDIA_DIR); 156269bff30SLei YU } 157269bff30SLei YU } 158269bff30SLei YU catch (const fs::filesystem_error& e) 159269bff30SLei YU { 160269bff30SLei YU log<level::ERR>("Failed to prepare dir", entry("ERR=%s", e.what())); 161269bff30SLei YU return; 162269bff30SLei YU } 163269bff30SLei YU 16488e8a325SGunnar Mills // Read os-release from /etc/ to get the functional BMC version 16588e8a325SGunnar Mills auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE); 16688e8a325SGunnar Mills 1671eef62deSSaqib Khan // Read os-release from folders under /media/ to get 1681eef62deSSaqib Khan // BMC Software Versions. 1691eef62deSSaqib Khan for (const auto& iter : fs::directory_iterator(MEDIA_DIR)) 1701eef62deSSaqib Khan { 1711eef62deSSaqib Khan auto activationState = server::Activation::Activations::Active; 1726fab70daSSaqib Khan static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX); 1731eef62deSSaqib Khan 1741eef62deSSaqib Khan // Check if the BMC_RO_PREFIXis the prefix of the iter.path 1752285fe0fSAdriana Kobylak if (0 == 1762285fe0fSAdriana Kobylak iter.path().native().compare(0, BMC_RO_PREFIX_LEN, BMC_ROFS_PREFIX)) 1771eef62deSSaqib Khan { 178021c365bSSaqib Khan // The versionId is extracted from the path 179021c365bSSaqib Khan // for example /media/ro-2a1022fe. 180021c365bSSaqib Khan auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN); 1811eef62deSSaqib Khan auto osRelease = iter.path() / OS_RELEASE_FILE; 1821eef62deSSaqib Khan if (!fs::is_regular_file(osRelease)) 1831eef62deSSaqib Khan { 1842285fe0fSAdriana Kobylak log<level::ERR>( 1852285fe0fSAdriana Kobylak "Failed to read osRelease", 186596466b8SAdriana Kobylak entry("FILENAME=%s", osRelease.string().c_str())); 187021c365bSSaqib Khan ItemUpdater::erase(id); 188021c365bSSaqib Khan continue; 1891eef62deSSaqib Khan } 19088e8a325SGunnar Mills auto version = VersionClass::getBMCVersion(osRelease); 1911eef62deSSaqib Khan if (version.empty()) 1921eef62deSSaqib Khan { 1932285fe0fSAdriana Kobylak log<level::ERR>( 1942285fe0fSAdriana Kobylak "Failed to read version from osRelease", 195596466b8SAdriana Kobylak entry("FILENAME=%s", osRelease.string().c_str())); 1961eef62deSSaqib Khan activationState = server::Activation::Activations::Invalid; 1971eef62deSSaqib Khan } 198021c365bSSaqib Khan 1991eef62deSSaqib Khan auto purpose = server::Version::VersionPurpose::BMC; 200ec4eec34SAdriana Kobylak restorePurpose(id, purpose); 201ec4eec34SAdriana Kobylak 2021eef62deSSaqib Khan auto path = fs::path(SOFTWARE_OBJPATH) / id; 2031eef62deSSaqib Khan 204269bff30SLei YU // Create functional association if this is the functional 205269bff30SLei YU // version 20688e8a325SGunnar Mills if (version.compare(functionalVersion) == 0) 20788e8a325SGunnar Mills { 20888e8a325SGunnar Mills createFunctionalAssociation(path); 20988e8a325SGunnar Mills } 21088e8a325SGunnar Mills 21143b25cdeSGunnar Mills AssociationList associations = {}; 21243b25cdeSGunnar Mills 21343b25cdeSGunnar Mills if (activationState == server::Activation::Activations::Active) 21443b25cdeSGunnar Mills { 21543b25cdeSGunnar Mills // Create an association to the BMC inventory item 21643b25cdeSGunnar Mills associations.emplace_back(std::make_tuple( 2172285fe0fSAdriana Kobylak ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION, 21843b25cdeSGunnar Mills bmcInventoryPath)); 21943b25cdeSGunnar Mills 22043b25cdeSGunnar Mills // Create an active association since this image is active 22143b25cdeSGunnar Mills createActiveAssociation(path); 22243b25cdeSGunnar Mills } 22343b25cdeSGunnar Mills 224bbebec79SAppaRao Puli // All updateable firmware components must expose the updateable 225bbebec79SAppaRao Puli // association. 226bbebec79SAppaRao Puli createUpdateableAssociation(path); 227bbebec79SAppaRao Puli 228ee590c74SAdriana Kobylak // Create Version instance for this version. 229ee590c74SAdriana Kobylak auto versionPtr = std::make_unique<VersionClass>( 2302285fe0fSAdriana Kobylak bus, path, version, purpose, "", 2312285fe0fSAdriana Kobylak std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); 232ee590c74SAdriana Kobylak auto isVersionFunctional = versionPtr->isFunctional(); 233ee13e831SSaqib Khan if (!isVersionFunctional) 234ee13e831SSaqib Khan { 235ee13e831SSaqib Khan versionPtr->deleteObject = 236ee13e831SSaqib Khan std::make_unique<phosphor::software::manager::Delete>( 237ee13e831SSaqib Khan bus, path, *versionPtr); 238ee13e831SSaqib Khan } 2392285fe0fSAdriana Kobylak versions.insert(std::make_pair(id, std::move(versionPtr))); 240ee590c74SAdriana Kobylak 2411eef62deSSaqib Khan // Create Activation instance for this version. 242ee13e831SSaqib Khan activations.insert(std::make_pair( 2432285fe0fSAdriana Kobylak id, std::make_unique<Activation>( 2442285fe0fSAdriana Kobylak bus, path, *this, id, activationState, associations))); 2451eef62deSSaqib Khan 246269bff30SLei YU // If Active, create RedundancyPriority instance for this 247269bff30SLei YU // version. 2481eef62deSSaqib Khan if (activationState == server::Activation::Activations::Active) 2491eef62deSSaqib Khan { 2501eef62deSSaqib Khan uint8_t priority = std::numeric_limits<uint8_t>::max(); 251687e75e2SAdriana Kobylak if (!restorePriority(id, priority)) 2521eef62deSSaqib Khan { 253ee590c74SAdriana Kobylak if (isVersionFunctional) 254ee590c74SAdriana Kobylak { 255ee590c74SAdriana Kobylak priority = 0; 256ee590c74SAdriana Kobylak } 257ee590c74SAdriana Kobylak else 258ee590c74SAdriana Kobylak { 2591eef62deSSaqib Khan log<level::ERR>("Unable to restore priority from file.", 260596466b8SAdriana Kobylak entry("VERSIONID=%s", id.c_str())); 2611eef62deSSaqib Khan } 262ee590c74SAdriana Kobylak } 2631eef62deSSaqib Khan activations.find(id)->second->redundancyPriority = 2641eef62deSSaqib Khan std::make_unique<RedundancyPriority>( 2652285fe0fSAdriana Kobylak bus, path, *(activations.find(id)->second), priority, 266b77551cdSAdriana Kobylak false); 2671eef62deSSaqib Khan } 2681eef62deSSaqib Khan } 2691eef62deSSaqib Khan } 270dcbfa04aSSaqib Khan 271dcbfa04aSSaqib Khan // If there is no ubi volume for bmc version then read the /etc/os-release 272dcbfa04aSSaqib Khan // and create rofs-<versionId> under /media 273dcbfa04aSSaqib Khan if (activations.size() == 0) 274dcbfa04aSSaqib Khan { 275d16bcbd5SGunnar Mills auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE); 276dcbfa04aSSaqib Khan auto id = phosphor::software::manager::Version::getId(version); 277dcbfa04aSSaqib Khan auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/"; 278dcbfa04aSSaqib Khan try 279dcbfa04aSSaqib Khan { 280dcbfa04aSSaqib Khan if (!fs::is_directory(versionFileDir)) 281dcbfa04aSSaqib Khan { 282dcbfa04aSSaqib Khan fs::create_directories(versionFileDir); 283dcbfa04aSSaqib Khan } 284dcbfa04aSSaqib Khan auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE; 285dcbfa04aSSaqib Khan fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath); 286dcbfa04aSSaqib Khan ItemUpdater::processBMCImage(); 287dcbfa04aSSaqib Khan } 288dcbfa04aSSaqib Khan catch (const std::exception& e) 289dcbfa04aSSaqib Khan { 290dcbfa04aSSaqib Khan log<level::ERR>(e.what()); 291dcbfa04aSSaqib Khan } 292dcbfa04aSSaqib Khan } 293eaa1ee05SEddie James 294eaa1ee05SEddie James mirrorUbootToAlt(); 295ba239881SSaqib Khan return; 296ba239881SSaqib Khan } 297ba239881SSaqib Khan 2983526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId) 2993526ef73SLeonel Gonzalez { 3006d873715SEddie James // Find entry in versions map 3016d873715SEddie James auto it = versions.find(entryId); 3026d873715SEddie James if (it != versions.end()) 3036d873715SEddie James { 3040f88b5afSLei YU if (it->second->isFunctional() && ACTIVE_BMC_MAX_ALLOWED > 1) 3056d873715SEddie James { 3062285fe0fSAdriana Kobylak log<level::ERR>("Error: Version is currently running on the BMC. " 3072285fe0fSAdriana Kobylak "Unable to remove.", 3082285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 3096d873715SEddie James return; 3106d873715SEddie James } 3116d873715SEddie James 3126d873715SEddie James // Delete ReadOnly partitions if it's not active 3133526ef73SLeonel Gonzalez removeReadOnlyPartition(entryId); 314687e75e2SAdriana Kobylak removePersistDataDirectory(entryId); 315ee13e831SSaqib Khan 316ee13e831SSaqib Khan // Removing entry in versions map 317ee13e831SSaqib Khan this->versions.erase(entryId); 3186d873715SEddie James } 3196d873715SEddie James else 3206d873715SEddie James { 3216d873715SEddie James // Delete ReadOnly partitions even if we can't find the version 3226d873715SEddie James removeReadOnlyPartition(entryId); 323687e75e2SAdriana Kobylak removePersistDataDirectory(entryId); 3246d873715SEddie James 3252285fe0fSAdriana Kobylak log<level::ERR>("Error: Failed to find version in item updater " 3262285fe0fSAdriana Kobylak "versions map. Unable to remove.", 3272285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 3286d873715SEddie James } 3291eef62deSSaqib Khan 33056aaf454SLei YU helper.clearEntry(entryId); 3313526ef73SLeonel Gonzalez 3323526ef73SLeonel Gonzalez // Removing entry in activations map 3333526ef73SLeonel Gonzalez auto ita = activations.find(entryId); 3343526ef73SLeonel Gonzalez if (ita == activations.end()) 3353526ef73SLeonel Gonzalez { 3362285fe0fSAdriana Kobylak log<level::ERR>("Error: Failed to find version in item updater " 3372285fe0fSAdriana Kobylak "activations map. Unable to remove.", 3382285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 3393526ef73SLeonel Gonzalez } 340ee13e831SSaqib Khan else 341ee13e831SSaqib Khan { 342991af7ecSAdriana Kobylak removeAssociations(ita->second->path); 3433526ef73SLeonel Gonzalez this->activations.erase(entryId); 344ee13e831SSaqib Khan } 34549446ae9SSaqib Khan ItemUpdater::resetUbootEnvVars(); 346ee13e831SSaqib Khan return; 3473526ef73SLeonel Gonzalez } 3483526ef73SLeonel Gonzalez 349bc1bf3afSMichael Tritz void ItemUpdater::deleteAll() 350bc1bf3afSMichael Tritz { 35183cd21fbSAdriana Kobylak std::vector<std::string> deletableVersions; 35283cd21fbSAdriana Kobylak 353bc1bf3afSMichael Tritz for (const auto& versionIt : versions) 354bc1bf3afSMichael Tritz { 355bc1bf3afSMichael Tritz if (!versionIt.second->isFunctional()) 356bc1bf3afSMichael Tritz { 35783cd21fbSAdriana Kobylak deletableVersions.push_back(versionIt.first); 358bc1bf3afSMichael Tritz } 359bc1bf3afSMichael Tritz } 360bc1bf3afSMichael Tritz 36183cd21fbSAdriana Kobylak for (const auto& deletableIt : deletableVersions) 36283cd21fbSAdriana Kobylak { 36383cd21fbSAdriana Kobylak ItemUpdater::erase(deletableIt); 36483cd21fbSAdriana Kobylak } 36583cd21fbSAdriana Kobylak 36656aaf454SLei YU helper.cleanup(); 367bc1bf3afSMichael Tritz } 368bc1bf3afSMichael Tritz 3692285fe0fSAdriana Kobylak ItemUpdater::ActivationStatus 3702285fe0fSAdriana Kobylak ItemUpdater::validateSquashFSImage(const std::string& filePath) 37135e83f3eSSaqib Khan { 372b1cfdf99SMichael Tritz bool invalid = false; 37335e83f3eSSaqib Khan 374b1cfdf99SMichael Tritz for (auto& bmcImage : bmcImages) 375b1cfdf99SMichael Tritz { 37619177d3eSSaqib Khan fs::path file(filePath); 37735e83f3eSSaqib Khan file /= bmcImage; 37835e83f3eSSaqib Khan std::ifstream efile(file.c_str()); 379b1cfdf99SMichael Tritz if (efile.good() != 1) 38035e83f3eSSaqib Khan { 381b1cfdf99SMichael Tritz log<level::ERR>("Failed to find the BMC image.", 382b1cfdf99SMichael Tritz entry("IMAGE=%s", bmcImage.c_str())); 383b1cfdf99SMichael Tritz invalid = true; 38435e83f3eSSaqib Khan } 385b1cfdf99SMichael Tritz } 386b1cfdf99SMichael Tritz 387b1cfdf99SMichael Tritz if (invalid) 38835e83f3eSSaqib Khan { 38935e83f3eSSaqib Khan return ItemUpdater::ActivationStatus::invalid; 39035e83f3eSSaqib Khan } 391b1cfdf99SMichael Tritz 392b1cfdf99SMichael Tritz return ItemUpdater::ActivationStatus::ready; 39335e83f3eSSaqib Khan } 39435e83f3eSSaqib Khan 395bbcb7be1SAdriana Kobylak void ItemUpdater::savePriority(const std::string& versionId, uint8_t value) 396bbcb7be1SAdriana Kobylak { 397687e75e2SAdriana Kobylak storePriority(versionId, value); 398bbcb7be1SAdriana Kobylak helper.setEntry(versionId, value); 399bbcb7be1SAdriana Kobylak } 400bbcb7be1SAdriana Kobylak 401b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId) 4024c1aec09SSaqib Khan { 403b77551cdSAdriana Kobylak std::map<std::string, uint8_t> priorityMap; 404b77551cdSAdriana Kobylak 405b77551cdSAdriana Kobylak // Insert the requested version and priority, it may not exist yet. 406b77551cdSAdriana Kobylak priorityMap.insert(std::make_pair(versionId, value)); 407b77551cdSAdriana Kobylak 4084c1aec09SSaqib Khan for (const auto& intf : activations) 4094c1aec09SSaqib Khan { 4104c1aec09SSaqib Khan if (intf.second->redundancyPriority) 4114c1aec09SSaqib Khan { 412b77551cdSAdriana Kobylak priorityMap.insert(std::make_pair( 4132285fe0fSAdriana Kobylak intf.first, intf.second->redundancyPriority.get()->priority())); 414b77551cdSAdriana Kobylak } 415b77551cdSAdriana Kobylak } 416b77551cdSAdriana Kobylak 417b77551cdSAdriana Kobylak // Lambda function to compare 2 priority values, use <= to allow duplicates 4182285fe0fSAdriana Kobylak typedef std::function<bool(std::pair<std::string, uint8_t>, 4192285fe0fSAdriana Kobylak std::pair<std::string, uint8_t>)> 4202285fe0fSAdriana Kobylak cmpPriority; 4212285fe0fSAdriana Kobylak cmpPriority cmpPriorityFunc = 4222285fe0fSAdriana Kobylak [](std::pair<std::string, uint8_t> priority1, 4232285fe0fSAdriana Kobylak std::pair<std::string, uint8_t> priority2) { 424b77551cdSAdriana Kobylak return priority1.second <= priority2.second; 425b77551cdSAdriana Kobylak }; 426b77551cdSAdriana Kobylak 427b77551cdSAdriana Kobylak // Sort versions by ascending priority 428b77551cdSAdriana Kobylak std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet( 429b77551cdSAdriana Kobylak priorityMap.begin(), priorityMap.end(), cmpPriorityFunc); 430b77551cdSAdriana Kobylak 431b77551cdSAdriana Kobylak auto freePriorityValue = value; 432b77551cdSAdriana Kobylak for (auto& element : prioritySet) 433b77551cdSAdriana Kobylak { 434b77551cdSAdriana Kobylak if (element.first == versionId) 435b77551cdSAdriana Kobylak { 436b77551cdSAdriana Kobylak continue; 437b77551cdSAdriana Kobylak } 438b77551cdSAdriana Kobylak if (element.second == freePriorityValue) 439b77551cdSAdriana Kobylak { 440b77551cdSAdriana Kobylak ++freePriorityValue; 441b77551cdSAdriana Kobylak auto it = activations.find(element.first); 442b77551cdSAdriana Kobylak it->second->redundancyPriority.get()->sdbusPriority( 443b77551cdSAdriana Kobylak freePriorityValue); 4444c1aec09SSaqib Khan } 4454c1aec09SSaqib Khan } 446b77551cdSAdriana Kobylak 447b77551cdSAdriana Kobylak auto lowestVersion = prioritySet.begin()->first; 448b77551cdSAdriana Kobylak if (value == prioritySet.begin()->second) 449b77551cdSAdriana Kobylak { 450b77551cdSAdriana Kobylak lowestVersion = versionId; 4514c1aec09SSaqib Khan } 452b77551cdSAdriana Kobylak updateUbootEnvVars(lowestVersion); 4534c1aec09SSaqib Khan } 4544c1aec09SSaqib Khan 45537a59043SMichael Tritz void ItemUpdater::reset() 45637a59043SMichael Tritz { 457*60f5ccfdSGunnar Mills constexpr auto setFactoryResetWait = std::chrono::seconds(3); 45856aaf454SLei YU helper.factoryReset(); 45937a59043SMichael Tritz 460*60f5ccfdSGunnar Mills // Need to wait for env variables to complete, otherwise an immediate reboot 461*60f5ccfdSGunnar Mills // will not factory reset. 462*60f5ccfdSGunnar Mills std::this_thread::sleep_for(setFactoryResetWait); 463*60f5ccfdSGunnar Mills 46437a59043SMichael Tritz log<level::INFO>("BMC factory reset will take effect upon reboot."); 46537a59043SMichael Tritz } 46637a59043SMichael Tritz 4673526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId) 4683526ef73SLeonel Gonzalez { 46956aaf454SLei YU helper.removeVersion(versionId); 4703526ef73SLeonel Gonzalez } 4713526ef73SLeonel Gonzalez 4720129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value) 4730129d926SMichael Tritz { 4740129d926SMichael Tritz // enabling field mode is intended to be one way: false -> true 4750129d926SMichael Tritz if (value && !control::FieldMode::fieldModeEnabled()) 4760129d926SMichael Tritz { 4770129d926SMichael Tritz control::FieldMode::fieldModeEnabled(value); 4780129d926SMichael Tritz 47922848eceSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 48022848eceSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 48122848eceSAdriana Kobylak method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service", 48222848eceSAdriana Kobylak "replace"); 48322848eceSAdriana Kobylak bus.call_noreply(method); 48422848eceSAdriana Kobylak 48522848eceSAdriana Kobylak method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 48622848eceSAdriana Kobylak SYSTEMD_INTERFACE, "StopUnit"); 48722848eceSAdriana Kobylak method.append("usr-local.mount", "replace"); 48822848eceSAdriana Kobylak bus.call_noreply(method); 48922848eceSAdriana Kobylak 49022848eceSAdriana Kobylak std::vector<std::string> usrLocal = {"usr-local.mount"}; 49122848eceSAdriana Kobylak 49222848eceSAdriana Kobylak method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 49322848eceSAdriana Kobylak SYSTEMD_INTERFACE, "MaskUnitFiles"); 49422848eceSAdriana Kobylak method.append(usrLocal, false, true); 49522848eceSAdriana Kobylak bus.call_noreply(method); 4960129d926SMichael Tritz } 497d5b8f75cSAdriana Kobylak else if (!value && control::FieldMode::fieldModeEnabled()) 498d5b8f75cSAdriana Kobylak { 499d5b8f75cSAdriana Kobylak elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON( 500d5b8f75cSAdriana Kobylak "FieldMode is not allowed to be cleared")); 501d5b8f75cSAdriana Kobylak } 5020129d926SMichael Tritz 5030129d926SMichael Tritz return control::FieldMode::fieldModeEnabled(); 5040129d926SMichael Tritz } 5050129d926SMichael Tritz 5060129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus() 5070129d926SMichael Tritz { 508ff0b421dSMichael Tritz std::ifstream input("/dev/mtd/u-boot-env"); 5090129d926SMichael Tritz std::string envVar; 5100129d926SMichael Tritz std::getline(input, envVar); 5110129d926SMichael Tritz 5120129d926SMichael Tritz if (envVar.find("fieldmode=true") != std::string::npos) 5130129d926SMichael Tritz { 5140129d926SMichael Tritz ItemUpdater::fieldModeEnabled(true); 5150129d926SMichael Tritz } 5160129d926SMichael Tritz } 5170129d926SMichael Tritz 518b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath() 519b60add1eSGunnar Mills { 520b60add1eSGunnar Mills auto depth = 0; 5212285fe0fSAdriana Kobylak auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 5222285fe0fSAdriana Kobylak MAPPER_INTERFACE, "GetSubTreePaths"); 523b60add1eSGunnar Mills 5241254c628SAdriana Kobylak mapperCall.append(INVENTORY_PATH); 525b60add1eSGunnar Mills mapperCall.append(depth); 5261254c628SAdriana Kobylak std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE}; 527b60add1eSGunnar Mills mapperCall.append(filter); 528b60add1eSGunnar Mills 52987c78173SEd Tanous try 530b60add1eSGunnar Mills { 53187c78173SEd Tanous auto response = bus.call(mapperCall); 532b60add1eSGunnar Mills 533b60add1eSGunnar Mills using ObjectPaths = std::vector<std::string>; 534b60add1eSGunnar Mills ObjectPaths result; 535b60add1eSGunnar Mills response.read(result); 536b60add1eSGunnar Mills 5371254c628SAdriana Kobylak if (!result.empty()) 538b60add1eSGunnar Mills { 5391254c628SAdriana Kobylak bmcInventoryPath = result.front(); 540b60add1eSGunnar Mills } 54187c78173SEd Tanous } 54287c78173SEd Tanous catch (const sdbusplus::exception::SdBusError& e) 54387c78173SEd Tanous { 54487c78173SEd Tanous log<level::ERR>("Error in mapper GetSubTreePath"); 54587c78173SEd Tanous return; 54687c78173SEd Tanous } 547b60add1eSGunnar Mills 548b60add1eSGunnar Mills return; 549b60add1eSGunnar Mills } 550b60add1eSGunnar Mills 551f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path) 552ded875dcSGunnar Mills { 5532285fe0fSAdriana Kobylak assocs.emplace_back( 5542285fe0fSAdriana Kobylak std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path)); 555ded875dcSGunnar Mills associations(assocs); 556ded875dcSGunnar Mills } 557ded875dcSGunnar Mills 55888e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path) 55988e8a325SGunnar Mills { 56088e8a325SGunnar Mills assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION, 5612285fe0fSAdriana Kobylak FUNCTIONAL_REV_ASSOCIATION, path)); 56288e8a325SGunnar Mills associations(assocs); 56388e8a325SGunnar Mills } 56488e8a325SGunnar Mills 565bbebec79SAppaRao Puli void ItemUpdater::createUpdateableAssociation(const std::string& path) 566bbebec79SAppaRao Puli { 567bbebec79SAppaRao Puli assocs.emplace_back(std::make_tuple(UPDATEABLE_FWD_ASSOCIATION, 568bbebec79SAppaRao Puli UPDATEABLE_REV_ASSOCIATION, path)); 569bbebec79SAppaRao Puli associations(assocs); 570bbebec79SAppaRao Puli } 571bbebec79SAppaRao Puli 572991af7ecSAdriana Kobylak void ItemUpdater::removeAssociations(const std::string& path) 573ded875dcSGunnar Mills { 574ded875dcSGunnar Mills for (auto iter = assocs.begin(); iter != assocs.end();) 575ded875dcSGunnar Mills { 576991af7ecSAdriana Kobylak if ((std::get<2>(*iter)).compare(path) == 0) 577ded875dcSGunnar Mills { 578ded875dcSGunnar Mills iter = assocs.erase(iter); 579ded875dcSGunnar Mills associations(assocs); 580ded875dcSGunnar Mills } 581ded875dcSGunnar Mills else 582ded875dcSGunnar Mills { 583ded875dcSGunnar Mills ++iter; 584ded875dcSGunnar Mills } 585ded875dcSGunnar Mills } 586ded875dcSGunnar Mills } 587ded875dcSGunnar Mills 588b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value) 589b9da6634SSaqib Khan { 590b9da6634SSaqib Khan for (const auto& intf : activations) 591b9da6634SSaqib Khan { 592b9da6634SSaqib Khan if (intf.second->redundancyPriority) 593b9da6634SSaqib Khan { 594b9da6634SSaqib Khan if (intf.second->redundancyPriority.get()->priority() < value) 595b9da6634SSaqib Khan { 596b9da6634SSaqib Khan return false; 597b9da6634SSaqib Khan } 598b9da6634SSaqib Khan } 599b9da6634SSaqib Khan } 600b9da6634SSaqib Khan return true; 601b9da6634SSaqib Khan } 602b9da6634SSaqib Khan 603b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId) 604b77551cdSAdriana Kobylak { 60556aaf454SLei YU helper.updateUbootVersionId(versionId); 606b77551cdSAdriana Kobylak } 607b77551cdSAdriana Kobylak 60849446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars() 60949446ae9SSaqib Khan { 61049446ae9SSaqib Khan decltype(activations.begin()->second->redundancyPriority.get()->priority()) 61149446ae9SSaqib Khan lowestPriority = std::numeric_limits<uint8_t>::max(); 61249446ae9SSaqib Khan decltype(activations.begin()->second->versionId) lowestPriorityVersion; 61349446ae9SSaqib Khan for (const auto& intf : activations) 61449446ae9SSaqib Khan { 61549446ae9SSaqib Khan if (!intf.second->redundancyPriority.get()) 61649446ae9SSaqib Khan { 61749446ae9SSaqib Khan // Skip this version if the redundancyPriority is not initialized. 61849446ae9SSaqib Khan continue; 61949446ae9SSaqib Khan } 62049446ae9SSaqib Khan 6212285fe0fSAdriana Kobylak if (intf.second->redundancyPriority.get()->priority() <= lowestPriority) 62249446ae9SSaqib Khan { 62349446ae9SSaqib Khan lowestPriority = intf.second->redundancyPriority.get()->priority(); 62449446ae9SSaqib Khan lowestPriorityVersion = intf.second->versionId; 62549446ae9SSaqib Khan } 62649446ae9SSaqib Khan } 62749446ae9SSaqib Khan 628f0382c35SSaqib Khan // Update the U-boot environment variable to point to the lowest priority 629b77551cdSAdriana Kobylak updateUbootEnvVars(lowestPriorityVersion); 63049446ae9SSaqib Khan } 63149446ae9SSaqib Khan 632a6963590SAdriana Kobylak void ItemUpdater::freeSpace(Activation& caller) 633204e1e74SAdriana Kobylak { 634204e1e74SAdriana Kobylak // Versions with the highest priority in front 635204e1e74SAdriana Kobylak std::priority_queue<std::pair<int, std::string>, 636204e1e74SAdriana Kobylak std::vector<std::pair<int, std::string>>, 6372285fe0fSAdriana Kobylak std::less<std::pair<int, std::string>>> 6382285fe0fSAdriana Kobylak versionsPQ; 639204e1e74SAdriana Kobylak 640204e1e74SAdriana Kobylak std::size_t count = 0; 641204e1e74SAdriana Kobylak for (const auto& iter : activations) 642204e1e74SAdriana Kobylak { 643204e1e74SAdriana Kobylak if ((iter.second.get()->activation() == 644204e1e74SAdriana Kobylak server::Activation::Activations::Active) || 645204e1e74SAdriana Kobylak (iter.second.get()->activation() == 646204e1e74SAdriana Kobylak server::Activation::Activations::Failed)) 647204e1e74SAdriana Kobylak { 648204e1e74SAdriana Kobylak count++; 649204e1e74SAdriana Kobylak // Don't put the functional version on the queue since we can't 650204e1e74SAdriana Kobylak // remove the "running" BMC version. 6510f88b5afSLei YU // If ACTIVE_BMC_MAX_ALLOWED <= 1, there is only one active BMC, 6520f88b5afSLei YU // so remove functional version as well. 653a6963590SAdriana Kobylak // Don't delete the the Activation object that called this function. 654a6963590SAdriana Kobylak if ((versions.find(iter.second->versionId) 655a6963590SAdriana Kobylak ->second->isFunctional() && 656a6963590SAdriana Kobylak ACTIVE_BMC_MAX_ALLOWED > 1) || 657a6963590SAdriana Kobylak (iter.second->versionId == caller.versionId)) 658204e1e74SAdriana Kobylak { 659204e1e74SAdriana Kobylak continue; 660204e1e74SAdriana Kobylak } 661a6963590SAdriana Kobylak 662a6963590SAdriana Kobylak // Failed activations don't have priority, assign them a large value 663a6963590SAdriana Kobylak // for sorting purposes. 664a6963590SAdriana Kobylak auto priority = 999; 665a6963590SAdriana Kobylak if (iter.second.get()->activation() == 666a6963590SAdriana Kobylak server::Activation::Activations::Active) 667a6963590SAdriana Kobylak { 668a6963590SAdriana Kobylak priority = iter.second->redundancyPriority.get()->priority(); 669a6963590SAdriana Kobylak } 670a6963590SAdriana Kobylak 671a6963590SAdriana Kobylak versionsPQ.push(std::make_pair(priority, iter.second->versionId)); 672204e1e74SAdriana Kobylak } 673204e1e74SAdriana Kobylak } 674204e1e74SAdriana Kobylak 675204e1e74SAdriana Kobylak // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1, 676204e1e74SAdriana Kobylak // remove the highest priority one(s). 677204e1e74SAdriana Kobylak while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty())) 678204e1e74SAdriana Kobylak { 679204e1e74SAdriana Kobylak erase(versionsPQ.top().second); 680204e1e74SAdriana Kobylak versionsPQ.pop(); 681204e1e74SAdriana Kobylak count--; 682204e1e74SAdriana Kobylak } 683204e1e74SAdriana Kobylak } 684204e1e74SAdriana Kobylak 685eaa1ee05SEddie James void ItemUpdater::mirrorUbootToAlt() 686eaa1ee05SEddie James { 68756aaf454SLei YU helper.mirrorAlt(); 688eaa1ee05SEddie James } 689eaa1ee05SEddie James 690ec1b41c4SGunnar Mills } // namespace updater 691ec1b41c4SGunnar Mills } // namespace software 692ec1b41c4SGunnar Mills } // namespace phosphor 693