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 10d5b8f75cSAdriana Kobylak #include <phosphor-logging/elog-errors.hpp> 11b0ce996aSGunnar Mills #include <phosphor-logging/elog.hpp> 12b0ce996aSGunnar Mills #include <phosphor-logging/log.hpp> 1358aa7508SAdriana Kobylak #include <xyz/openbmc_project/Common/error.hpp> 1458aa7508SAdriana Kobylak #include <xyz/openbmc_project/Software/Image/error.hpp> 1558aa7508SAdriana Kobylak 1658aa7508SAdriana Kobylak #include <filesystem> 1758aa7508SAdriana Kobylak #include <fstream> 18204e1e74SAdriana Kobylak #include <queue> 19b77551cdSAdriana Kobylak #include <set> 20ec1b41c4SGunnar Mills #include <string> 2160f5ccfdSGunnar Mills #include <thread> 22ec1b41c4SGunnar Mills 23ec1b41c4SGunnar Mills namespace phosphor 24ec1b41c4SGunnar Mills { 25ec1b41c4SGunnar Mills namespace software 26ec1b41c4SGunnar Mills { 27ec1b41c4SGunnar Mills namespace updater 28ec1b41c4SGunnar Mills { 29ec1b41c4SGunnar Mills 302ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class 312ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server; 320129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server; 332ce7da29SGunnar Mills 342ce7da29SGunnar Mills using namespace phosphor::logging; 3543699ca7SAdriana Kobylak using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error; 362ab9b109SJayanth Othayoth using namespace phosphor::software::image; 37c98d912eSAdriana Kobylak namespace fs = std::filesystem; 38d5b8f75cSAdriana Kobylak using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 3935e83f3eSSaqib Khan 40e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg) 41ec1b41c4SGunnar Mills { 4284a0e693SSaqib Khan 4384a0e693SSaqib Khan using SVersion = server::Version; 4484a0e693SSaqib Khan using VersionPurpose = SVersion::VersionPurpose; 459a782243SGunnar Mills using VersionClass = phosphor::software::manager::Version; 4684a0e693SSaqib Khan 47bc1facd7SPatrick Williams sdbusplus::message::object_path objPath; 4884a0e693SSaqib Khan auto purpose = VersionPurpose::Unknown; 49705f1bfcSSaqib Khan std::string version; 50bc1facd7SPatrick Williams std::map<std::string, std::map<std::string, std::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 { 178716cd78dSAdriana Kobylak // Get the version to calculate the id 17924a8d83dSAdriana Kobylak fs::path releaseFile(OS_RELEASE_FILE); 18024a8d83dSAdriana Kobylak auto osRelease = iter.path() / releaseFile.relative_path(); 1811eef62deSSaqib Khan if (!fs::is_regular_file(osRelease)) 1821eef62deSSaqib Khan { 1832285fe0fSAdriana Kobylak log<level::ERR>( 1842285fe0fSAdriana Kobylak "Failed to read osRelease", 185596466b8SAdriana Kobylak entry("FILENAME=%s", osRelease.string().c_str())); 186716cd78dSAdriana Kobylak 187716cd78dSAdriana Kobylak // Try to get the version id from the mount directory name and 188716cd78dSAdriana Kobylak // call to delete it as this version may be corrupted. Dynamic 189716cd78dSAdriana Kobylak // volumes created by the UBI layout for example have the id in 190716cd78dSAdriana Kobylak // the mount directory name. The worst that can happen is that 191716cd78dSAdriana Kobylak // erase() is called with an non-existent id and returns. 192716cd78dSAdriana Kobylak auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN); 193021c365bSSaqib Khan ItemUpdater::erase(id); 194716cd78dSAdriana Kobylak 195021c365bSSaqib Khan continue; 1961eef62deSSaqib Khan } 19788e8a325SGunnar Mills auto version = VersionClass::getBMCVersion(osRelease); 1981eef62deSSaqib Khan if (version.empty()) 1991eef62deSSaqib Khan { 2002285fe0fSAdriana Kobylak log<level::ERR>( 2012285fe0fSAdriana Kobylak "Failed to read version from osRelease", 202596466b8SAdriana Kobylak entry("FILENAME=%s", osRelease.string().c_str())); 203716cd78dSAdriana Kobylak 204716cd78dSAdriana Kobylak // Try to delete the version, same as above if the 205716cd78dSAdriana Kobylak // OS_RELEASE_FILE does not exist. 206716cd78dSAdriana Kobylak auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN); 207716cd78dSAdriana Kobylak ItemUpdater::erase(id); 208716cd78dSAdriana Kobylak 209716cd78dSAdriana Kobylak continue; 2101eef62deSSaqib Khan } 211021c365bSSaqib Khan 212716cd78dSAdriana Kobylak auto id = VersionClass::getId(version); 213716cd78dSAdriana Kobylak 214f383d27aSAdriana Kobylak // Check if the id has already been added. This can happen if the 215f383d27aSAdriana Kobylak // BMC partitions / devices were manually flashed with the same 216f383d27aSAdriana Kobylak // image. 217f383d27aSAdriana Kobylak if (versions.find(id) != versions.end()) 218f383d27aSAdriana Kobylak { 219f383d27aSAdriana Kobylak continue; 220f383d27aSAdriana Kobylak } 221f383d27aSAdriana Kobylak 2221eef62deSSaqib Khan auto purpose = server::Version::VersionPurpose::BMC; 223ec4eec34SAdriana Kobylak restorePurpose(id, purpose); 224ec4eec34SAdriana Kobylak 2251eef62deSSaqib Khan auto path = fs::path(SOFTWARE_OBJPATH) / id; 2261eef62deSSaqib Khan 227269bff30SLei YU // Create functional association if this is the functional 228269bff30SLei YU // version 22988e8a325SGunnar Mills if (version.compare(functionalVersion) == 0) 23088e8a325SGunnar Mills { 23188e8a325SGunnar Mills createFunctionalAssociation(path); 23288e8a325SGunnar Mills } 23388e8a325SGunnar Mills 23443b25cdeSGunnar Mills AssociationList associations = {}; 23543b25cdeSGunnar Mills 23643b25cdeSGunnar Mills if (activationState == server::Activation::Activations::Active) 23743b25cdeSGunnar Mills { 23843b25cdeSGunnar Mills // Create an association to the BMC inventory item 23943b25cdeSGunnar Mills associations.emplace_back(std::make_tuple( 2402285fe0fSAdriana Kobylak ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION, 24143b25cdeSGunnar Mills bmcInventoryPath)); 24243b25cdeSGunnar Mills 24343b25cdeSGunnar Mills // Create an active association since this image is active 24443b25cdeSGunnar Mills createActiveAssociation(path); 24543b25cdeSGunnar Mills } 24643b25cdeSGunnar Mills 247bbebec79SAppaRao Puli // All updateable firmware components must expose the updateable 248bbebec79SAppaRao Puli // association. 249bbebec79SAppaRao Puli createUpdateableAssociation(path); 250bbebec79SAppaRao Puli 251ee590c74SAdriana Kobylak // Create Version instance for this version. 252ee590c74SAdriana Kobylak auto versionPtr = std::make_unique<VersionClass>( 2532285fe0fSAdriana Kobylak bus, path, version, purpose, "", 2542285fe0fSAdriana Kobylak std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); 255ee590c74SAdriana Kobylak auto isVersionFunctional = versionPtr->isFunctional(); 256ee13e831SSaqib Khan if (!isVersionFunctional) 257ee13e831SSaqib Khan { 258ee13e831SSaqib Khan versionPtr->deleteObject = 259ee13e831SSaqib Khan std::make_unique<phosphor::software::manager::Delete>( 260ee13e831SSaqib Khan bus, path, *versionPtr); 261ee13e831SSaqib Khan } 2622285fe0fSAdriana Kobylak versions.insert(std::make_pair(id, std::move(versionPtr))); 263ee590c74SAdriana Kobylak 2641eef62deSSaqib Khan // Create Activation instance for this version. 265ee13e831SSaqib Khan activations.insert(std::make_pair( 2662285fe0fSAdriana Kobylak id, std::make_unique<Activation>( 2672285fe0fSAdriana Kobylak bus, path, *this, id, activationState, associations))); 2681eef62deSSaqib Khan 269269bff30SLei YU // If Active, create RedundancyPriority instance for this 270269bff30SLei YU // version. 2711eef62deSSaqib Khan if (activationState == server::Activation::Activations::Active) 2721eef62deSSaqib Khan { 2731eef62deSSaqib Khan uint8_t priority = std::numeric_limits<uint8_t>::max(); 274687e75e2SAdriana Kobylak if (!restorePriority(id, priority)) 2751eef62deSSaqib Khan { 276ee590c74SAdriana Kobylak if (isVersionFunctional) 277ee590c74SAdriana Kobylak { 278ee590c74SAdriana Kobylak priority = 0; 279ee590c74SAdriana Kobylak } 280ee590c74SAdriana Kobylak else 281ee590c74SAdriana Kobylak { 2821eef62deSSaqib Khan log<level::ERR>("Unable to restore priority from file.", 283596466b8SAdriana Kobylak entry("VERSIONID=%s", id.c_str())); 2841eef62deSSaqib Khan } 285ee590c74SAdriana Kobylak } 2861eef62deSSaqib Khan activations.find(id)->second->redundancyPriority = 2871eef62deSSaqib Khan std::make_unique<RedundancyPriority>( 2882285fe0fSAdriana Kobylak bus, path, *(activations.find(id)->second), priority, 289b77551cdSAdriana Kobylak false); 2901eef62deSSaqib Khan } 2911eef62deSSaqib Khan } 2921eef62deSSaqib Khan } 293dcbfa04aSSaqib Khan 294716cd78dSAdriana Kobylak // If there are no bmc versions mounted under MEDIA_DIR, then read the 295716cd78dSAdriana Kobylak // /etc/os-release and create rofs-<versionId> under MEDIA_DIR, then call 296716cd78dSAdriana Kobylak // again processBMCImage() to create the D-Bus interface for it. 297dcbfa04aSSaqib Khan if (activations.size() == 0) 298dcbfa04aSSaqib Khan { 299d16bcbd5SGunnar Mills auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE); 300dcbfa04aSSaqib Khan auto id = phosphor::software::manager::Version::getId(version); 301dcbfa04aSSaqib Khan auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/"; 302dcbfa04aSSaqib Khan try 303dcbfa04aSSaqib Khan { 304dcbfa04aSSaqib Khan if (!fs::is_directory(versionFileDir)) 305dcbfa04aSSaqib Khan { 306dcbfa04aSSaqib Khan fs::create_directories(versionFileDir); 307dcbfa04aSSaqib Khan } 308dcbfa04aSSaqib Khan auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE; 309dcbfa04aSSaqib Khan fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath); 310dcbfa04aSSaqib Khan ItemUpdater::processBMCImage(); 311dcbfa04aSSaqib Khan } 312dcbfa04aSSaqib Khan catch (const std::exception& e) 313dcbfa04aSSaqib Khan { 314dcbfa04aSSaqib Khan log<level::ERR>(e.what()); 315dcbfa04aSSaqib Khan } 316dcbfa04aSSaqib Khan } 317eaa1ee05SEddie James 318eaa1ee05SEddie James mirrorUbootToAlt(); 319ba239881SSaqib Khan return; 320ba239881SSaqib Khan } 321ba239881SSaqib Khan 3223526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId) 3233526ef73SLeonel Gonzalez { 3246d873715SEddie James // Find entry in versions map 3256d873715SEddie James auto it = versions.find(entryId); 3266d873715SEddie James if (it != versions.end()) 3276d873715SEddie James { 3280f88b5afSLei YU if (it->second->isFunctional() && ACTIVE_BMC_MAX_ALLOWED > 1) 3296d873715SEddie James { 3302285fe0fSAdriana Kobylak log<level::ERR>("Error: Version is currently running on the BMC. " 3312285fe0fSAdriana Kobylak "Unable to remove.", 3322285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 3336d873715SEddie James return; 3346d873715SEddie James } 335*d1a55adcSAdriana Kobylak } 3366d873715SEddie James 337*d1a55adcSAdriana Kobylak // First call resetUbootEnvVars() so that the BMC points to a valid image to 338*d1a55adcSAdriana Kobylak // boot from. If resetUbootEnvVars() is called after the image is actually 339*d1a55adcSAdriana Kobylak // deleted from the BMC flash, there'd be a time window where the BMC would 340*d1a55adcSAdriana Kobylak // be pointing to a non-existent image to boot from. 341*d1a55adcSAdriana Kobylak // Need to remove the entries from the activations map before that call so 342*d1a55adcSAdriana Kobylak // that resetUbootEnvVars() doesn't use the version to be deleted. 343*d1a55adcSAdriana Kobylak auto iteratorActivations = activations.find(entryId); 344*d1a55adcSAdriana Kobylak if (iteratorActivations == activations.end()) 345*d1a55adcSAdriana Kobylak { 346*d1a55adcSAdriana Kobylak log<level::ERR>("Error: Failed to find version in item updater " 347*d1a55adcSAdriana Kobylak "activations map. Unable to remove.", 348*d1a55adcSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 349*d1a55adcSAdriana Kobylak } 350*d1a55adcSAdriana Kobylak else 351*d1a55adcSAdriana Kobylak { 352*d1a55adcSAdriana Kobylak removeAssociations(iteratorActivations->second->path); 353*d1a55adcSAdriana Kobylak this->activations.erase(entryId); 354*d1a55adcSAdriana Kobylak } 355*d1a55adcSAdriana Kobylak ItemUpdater::resetUbootEnvVars(); 356*d1a55adcSAdriana Kobylak 357*d1a55adcSAdriana Kobylak if (it != versions.end()) 358*d1a55adcSAdriana Kobylak { 3596d873715SEddie James // Delete ReadOnly partitions if it's not active 3603526ef73SLeonel Gonzalez removeReadOnlyPartition(entryId); 361687e75e2SAdriana Kobylak removePersistDataDirectory(entryId); 362ee13e831SSaqib Khan 363ee13e831SSaqib Khan // Removing entry in versions map 364ee13e831SSaqib Khan this->versions.erase(entryId); 3656d873715SEddie James } 3666d873715SEddie James else 3676d873715SEddie James { 3686d873715SEddie James // Delete ReadOnly partitions even if we can't find the version 3696d873715SEddie James removeReadOnlyPartition(entryId); 370687e75e2SAdriana Kobylak removePersistDataDirectory(entryId); 3716d873715SEddie James 3722285fe0fSAdriana Kobylak log<level::ERR>("Error: Failed to find version in item updater " 3732285fe0fSAdriana Kobylak "versions map. Unable to remove.", 3742285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 3756d873715SEddie James } 3761eef62deSSaqib Khan 37756aaf454SLei YU helper.clearEntry(entryId); 3783526ef73SLeonel Gonzalez 379ee13e831SSaqib Khan return; 3803526ef73SLeonel Gonzalez } 3813526ef73SLeonel Gonzalez 382bc1bf3afSMichael Tritz void ItemUpdater::deleteAll() 383bc1bf3afSMichael Tritz { 38483cd21fbSAdriana Kobylak std::vector<std::string> deletableVersions; 38583cd21fbSAdriana Kobylak 386bc1bf3afSMichael Tritz for (const auto& versionIt : versions) 387bc1bf3afSMichael Tritz { 388bc1bf3afSMichael Tritz if (!versionIt.second->isFunctional()) 389bc1bf3afSMichael Tritz { 39083cd21fbSAdriana Kobylak deletableVersions.push_back(versionIt.first); 391bc1bf3afSMichael Tritz } 392bc1bf3afSMichael Tritz } 393bc1bf3afSMichael Tritz 39483cd21fbSAdriana Kobylak for (const auto& deletableIt : deletableVersions) 39583cd21fbSAdriana Kobylak { 39683cd21fbSAdriana Kobylak ItemUpdater::erase(deletableIt); 39783cd21fbSAdriana Kobylak } 39883cd21fbSAdriana Kobylak 39956aaf454SLei YU helper.cleanup(); 400bc1bf3afSMichael Tritz } 401bc1bf3afSMichael Tritz 4022285fe0fSAdriana Kobylak ItemUpdater::ActivationStatus 4032285fe0fSAdriana Kobylak ItemUpdater::validateSquashFSImage(const std::string& filePath) 40435e83f3eSSaqib Khan { 405b1cfdf99SMichael Tritz bool invalid = false; 40635e83f3eSSaqib Khan 407b1cfdf99SMichael Tritz for (auto& bmcImage : bmcImages) 408b1cfdf99SMichael Tritz { 40919177d3eSSaqib Khan fs::path file(filePath); 41035e83f3eSSaqib Khan file /= bmcImage; 41135e83f3eSSaqib Khan std::ifstream efile(file.c_str()); 412b1cfdf99SMichael Tritz if (efile.good() != 1) 41335e83f3eSSaqib Khan { 414b1cfdf99SMichael Tritz log<level::ERR>("Failed to find the BMC image.", 415b1cfdf99SMichael Tritz entry("IMAGE=%s", bmcImage.c_str())); 416b1cfdf99SMichael Tritz invalid = true; 41735e83f3eSSaqib Khan } 418b1cfdf99SMichael Tritz } 419b1cfdf99SMichael Tritz 420b1cfdf99SMichael Tritz if (invalid) 42135e83f3eSSaqib Khan { 42235e83f3eSSaqib Khan return ItemUpdater::ActivationStatus::invalid; 42335e83f3eSSaqib Khan } 424b1cfdf99SMichael Tritz 425b1cfdf99SMichael Tritz return ItemUpdater::ActivationStatus::ready; 42635e83f3eSSaqib Khan } 42735e83f3eSSaqib Khan 428bbcb7be1SAdriana Kobylak void ItemUpdater::savePriority(const std::string& versionId, uint8_t value) 429bbcb7be1SAdriana Kobylak { 430687e75e2SAdriana Kobylak storePriority(versionId, value); 431bbcb7be1SAdriana Kobylak helper.setEntry(versionId, value); 432bbcb7be1SAdriana Kobylak } 433bbcb7be1SAdriana Kobylak 434b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId) 4354c1aec09SSaqib Khan { 436b77551cdSAdriana Kobylak std::map<std::string, uint8_t> priorityMap; 437b77551cdSAdriana Kobylak 438b77551cdSAdriana Kobylak // Insert the requested version and priority, it may not exist yet. 439b77551cdSAdriana Kobylak priorityMap.insert(std::make_pair(versionId, value)); 440b77551cdSAdriana Kobylak 4414c1aec09SSaqib Khan for (const auto& intf : activations) 4424c1aec09SSaqib Khan { 4434c1aec09SSaqib Khan if (intf.second->redundancyPriority) 4444c1aec09SSaqib Khan { 445b77551cdSAdriana Kobylak priorityMap.insert(std::make_pair( 4462285fe0fSAdriana Kobylak intf.first, intf.second->redundancyPriority.get()->priority())); 447b77551cdSAdriana Kobylak } 448b77551cdSAdriana Kobylak } 449b77551cdSAdriana Kobylak 450b77551cdSAdriana Kobylak // Lambda function to compare 2 priority values, use <= to allow duplicates 4512285fe0fSAdriana Kobylak typedef std::function<bool(std::pair<std::string, uint8_t>, 4522285fe0fSAdriana Kobylak std::pair<std::string, uint8_t>)> 4532285fe0fSAdriana Kobylak cmpPriority; 4542285fe0fSAdriana Kobylak cmpPriority cmpPriorityFunc = 4552285fe0fSAdriana Kobylak [](std::pair<std::string, uint8_t> priority1, 4562285fe0fSAdriana Kobylak std::pair<std::string, uint8_t> priority2) { 457b77551cdSAdriana Kobylak return priority1.second <= priority2.second; 458b77551cdSAdriana Kobylak }; 459b77551cdSAdriana Kobylak 460b77551cdSAdriana Kobylak // Sort versions by ascending priority 461b77551cdSAdriana Kobylak std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet( 462b77551cdSAdriana Kobylak priorityMap.begin(), priorityMap.end(), cmpPriorityFunc); 463b77551cdSAdriana Kobylak 464b77551cdSAdriana Kobylak auto freePriorityValue = value; 465b77551cdSAdriana Kobylak for (auto& element : prioritySet) 466b77551cdSAdriana Kobylak { 467b77551cdSAdriana Kobylak if (element.first == versionId) 468b77551cdSAdriana Kobylak { 469b77551cdSAdriana Kobylak continue; 470b77551cdSAdriana Kobylak } 471b77551cdSAdriana Kobylak if (element.second == freePriorityValue) 472b77551cdSAdriana Kobylak { 473b77551cdSAdriana Kobylak ++freePriorityValue; 474b77551cdSAdriana Kobylak auto it = activations.find(element.first); 475b77551cdSAdriana Kobylak it->second->redundancyPriority.get()->sdbusPriority( 476b77551cdSAdriana Kobylak freePriorityValue); 4774c1aec09SSaqib Khan } 4784c1aec09SSaqib Khan } 479b77551cdSAdriana Kobylak 480b77551cdSAdriana Kobylak auto lowestVersion = prioritySet.begin()->first; 481b77551cdSAdriana Kobylak if (value == prioritySet.begin()->second) 482b77551cdSAdriana Kobylak { 483b77551cdSAdriana Kobylak lowestVersion = versionId; 4844c1aec09SSaqib Khan } 485b77551cdSAdriana Kobylak updateUbootEnvVars(lowestVersion); 4864c1aec09SSaqib Khan } 4874c1aec09SSaqib Khan 48837a59043SMichael Tritz void ItemUpdater::reset() 48937a59043SMichael Tritz { 49060f5ccfdSGunnar Mills constexpr auto setFactoryResetWait = std::chrono::seconds(3); 49156aaf454SLei YU helper.factoryReset(); 49237a59043SMichael Tritz 49360f5ccfdSGunnar Mills // Need to wait for env variables to complete, otherwise an immediate reboot 49460f5ccfdSGunnar Mills // will not factory reset. 49560f5ccfdSGunnar Mills std::this_thread::sleep_for(setFactoryResetWait); 49660f5ccfdSGunnar Mills 49737a59043SMichael Tritz log<level::INFO>("BMC factory reset will take effect upon reboot."); 49837a59043SMichael Tritz } 49937a59043SMichael Tritz 5003526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId) 5013526ef73SLeonel Gonzalez { 50256aaf454SLei YU helper.removeVersion(versionId); 5033526ef73SLeonel Gonzalez } 5043526ef73SLeonel Gonzalez 5050129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value) 5060129d926SMichael Tritz { 5070129d926SMichael Tritz // enabling field mode is intended to be one way: false -> true 5080129d926SMichael Tritz if (value && !control::FieldMode::fieldModeEnabled()) 5090129d926SMichael Tritz { 5100129d926SMichael Tritz control::FieldMode::fieldModeEnabled(value); 5110129d926SMichael Tritz 51222848eceSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 51322848eceSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 51422848eceSAdriana Kobylak method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service", 51522848eceSAdriana Kobylak "replace"); 51622848eceSAdriana Kobylak bus.call_noreply(method); 51722848eceSAdriana Kobylak 51822848eceSAdriana Kobylak method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 51922848eceSAdriana Kobylak SYSTEMD_INTERFACE, "StopUnit"); 52022848eceSAdriana Kobylak method.append("usr-local.mount", "replace"); 52122848eceSAdriana Kobylak bus.call_noreply(method); 52222848eceSAdriana Kobylak 52322848eceSAdriana Kobylak std::vector<std::string> usrLocal = {"usr-local.mount"}; 52422848eceSAdriana Kobylak 52522848eceSAdriana Kobylak method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 52622848eceSAdriana Kobylak SYSTEMD_INTERFACE, "MaskUnitFiles"); 52722848eceSAdriana Kobylak method.append(usrLocal, false, true); 52822848eceSAdriana Kobylak bus.call_noreply(method); 5290129d926SMichael Tritz } 530d5b8f75cSAdriana Kobylak else if (!value && control::FieldMode::fieldModeEnabled()) 531d5b8f75cSAdriana Kobylak { 532d5b8f75cSAdriana Kobylak elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON( 533d5b8f75cSAdriana Kobylak "FieldMode is not allowed to be cleared")); 534d5b8f75cSAdriana Kobylak } 5350129d926SMichael Tritz 5360129d926SMichael Tritz return control::FieldMode::fieldModeEnabled(); 5370129d926SMichael Tritz } 5380129d926SMichael Tritz 5390129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus() 5400129d926SMichael Tritz { 541ff0b421dSMichael Tritz std::ifstream input("/dev/mtd/u-boot-env"); 5420129d926SMichael Tritz std::string envVar; 5430129d926SMichael Tritz std::getline(input, envVar); 5440129d926SMichael Tritz 5450129d926SMichael Tritz if (envVar.find("fieldmode=true") != std::string::npos) 5460129d926SMichael Tritz { 5470129d926SMichael Tritz ItemUpdater::fieldModeEnabled(true); 5480129d926SMichael Tritz } 5490129d926SMichael Tritz } 5500129d926SMichael Tritz 551b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath() 552b60add1eSGunnar Mills { 553b60add1eSGunnar Mills auto depth = 0; 5542285fe0fSAdriana Kobylak auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 5552285fe0fSAdriana Kobylak MAPPER_INTERFACE, "GetSubTreePaths"); 556b60add1eSGunnar Mills 5571254c628SAdriana Kobylak mapperCall.append(INVENTORY_PATH); 558b60add1eSGunnar Mills mapperCall.append(depth); 5591254c628SAdriana Kobylak std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE}; 560b60add1eSGunnar Mills mapperCall.append(filter); 561b60add1eSGunnar Mills 56287c78173SEd Tanous try 563b60add1eSGunnar Mills { 56487c78173SEd Tanous auto response = bus.call(mapperCall); 565b60add1eSGunnar Mills 566b60add1eSGunnar Mills using ObjectPaths = std::vector<std::string>; 567b60add1eSGunnar Mills ObjectPaths result; 568b60add1eSGunnar Mills response.read(result); 569b60add1eSGunnar Mills 5701254c628SAdriana Kobylak if (!result.empty()) 571b60add1eSGunnar Mills { 5721254c628SAdriana Kobylak bmcInventoryPath = result.front(); 573b60add1eSGunnar Mills } 57487c78173SEd Tanous } 57587c78173SEd Tanous catch (const sdbusplus::exception::SdBusError& e) 57687c78173SEd Tanous { 57787c78173SEd Tanous log<level::ERR>("Error in mapper GetSubTreePath"); 57887c78173SEd Tanous return; 57987c78173SEd Tanous } 580b60add1eSGunnar Mills 581b60add1eSGunnar Mills return; 582b60add1eSGunnar Mills } 583b60add1eSGunnar Mills 584f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path) 585ded875dcSGunnar Mills { 5862285fe0fSAdriana Kobylak assocs.emplace_back( 5872285fe0fSAdriana Kobylak std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path)); 588ded875dcSGunnar Mills associations(assocs); 589ded875dcSGunnar Mills } 590ded875dcSGunnar Mills 59188e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path) 59288e8a325SGunnar Mills { 59388e8a325SGunnar Mills assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION, 5942285fe0fSAdriana Kobylak FUNCTIONAL_REV_ASSOCIATION, path)); 59588e8a325SGunnar Mills associations(assocs); 59688e8a325SGunnar Mills } 59788e8a325SGunnar Mills 598bbebec79SAppaRao Puli void ItemUpdater::createUpdateableAssociation(const std::string& path) 599bbebec79SAppaRao Puli { 600bbebec79SAppaRao Puli assocs.emplace_back(std::make_tuple(UPDATEABLE_FWD_ASSOCIATION, 601bbebec79SAppaRao Puli UPDATEABLE_REV_ASSOCIATION, path)); 602bbebec79SAppaRao Puli associations(assocs); 603bbebec79SAppaRao Puli } 604bbebec79SAppaRao Puli 605991af7ecSAdriana Kobylak void ItemUpdater::removeAssociations(const std::string& path) 606ded875dcSGunnar Mills { 607ded875dcSGunnar Mills for (auto iter = assocs.begin(); iter != assocs.end();) 608ded875dcSGunnar Mills { 609991af7ecSAdriana Kobylak if ((std::get<2>(*iter)).compare(path) == 0) 610ded875dcSGunnar Mills { 611ded875dcSGunnar Mills iter = assocs.erase(iter); 612ded875dcSGunnar Mills associations(assocs); 613ded875dcSGunnar Mills } 614ded875dcSGunnar Mills else 615ded875dcSGunnar Mills { 616ded875dcSGunnar Mills ++iter; 617ded875dcSGunnar Mills } 618ded875dcSGunnar Mills } 619ded875dcSGunnar Mills } 620ded875dcSGunnar Mills 621b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value) 622b9da6634SSaqib Khan { 623b9da6634SSaqib Khan for (const auto& intf : activations) 624b9da6634SSaqib Khan { 625b9da6634SSaqib Khan if (intf.second->redundancyPriority) 626b9da6634SSaqib Khan { 627b9da6634SSaqib Khan if (intf.second->redundancyPriority.get()->priority() < value) 628b9da6634SSaqib Khan { 629b9da6634SSaqib Khan return false; 630b9da6634SSaqib Khan } 631b9da6634SSaqib Khan } 632b9da6634SSaqib Khan } 633b9da6634SSaqib Khan return true; 634b9da6634SSaqib Khan } 635b9da6634SSaqib Khan 636b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId) 637b77551cdSAdriana Kobylak { 63856aaf454SLei YU helper.updateUbootVersionId(versionId); 639b77551cdSAdriana Kobylak } 640b77551cdSAdriana Kobylak 64149446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars() 64249446ae9SSaqib Khan { 64349446ae9SSaqib Khan decltype(activations.begin()->second->redundancyPriority.get()->priority()) 64449446ae9SSaqib Khan lowestPriority = std::numeric_limits<uint8_t>::max(); 64549446ae9SSaqib Khan decltype(activations.begin()->second->versionId) lowestPriorityVersion; 64649446ae9SSaqib Khan for (const auto& intf : activations) 64749446ae9SSaqib Khan { 64849446ae9SSaqib Khan if (!intf.second->redundancyPriority.get()) 64949446ae9SSaqib Khan { 65049446ae9SSaqib Khan // Skip this version if the redundancyPriority is not initialized. 65149446ae9SSaqib Khan continue; 65249446ae9SSaqib Khan } 65349446ae9SSaqib Khan 6542285fe0fSAdriana Kobylak if (intf.second->redundancyPriority.get()->priority() <= lowestPriority) 65549446ae9SSaqib Khan { 65649446ae9SSaqib Khan lowestPriority = intf.second->redundancyPriority.get()->priority(); 65749446ae9SSaqib Khan lowestPriorityVersion = intf.second->versionId; 65849446ae9SSaqib Khan } 65949446ae9SSaqib Khan } 66049446ae9SSaqib Khan 661f0382c35SSaqib Khan // Update the U-boot environment variable to point to the lowest priority 662b77551cdSAdriana Kobylak updateUbootEnvVars(lowestPriorityVersion); 66349446ae9SSaqib Khan } 66449446ae9SSaqib Khan 665a6963590SAdriana Kobylak void ItemUpdater::freeSpace(Activation& caller) 666204e1e74SAdriana Kobylak { 667204e1e74SAdriana Kobylak // Versions with the highest priority in front 668204e1e74SAdriana Kobylak std::priority_queue<std::pair<int, std::string>, 669204e1e74SAdriana Kobylak std::vector<std::pair<int, std::string>>, 6702285fe0fSAdriana Kobylak std::less<std::pair<int, std::string>>> 6712285fe0fSAdriana Kobylak versionsPQ; 672204e1e74SAdriana Kobylak 673204e1e74SAdriana Kobylak std::size_t count = 0; 674204e1e74SAdriana Kobylak for (const auto& iter : activations) 675204e1e74SAdriana Kobylak { 676204e1e74SAdriana Kobylak if ((iter.second.get()->activation() == 677204e1e74SAdriana Kobylak server::Activation::Activations::Active) || 678204e1e74SAdriana Kobylak (iter.second.get()->activation() == 679204e1e74SAdriana Kobylak server::Activation::Activations::Failed)) 680204e1e74SAdriana Kobylak { 681204e1e74SAdriana Kobylak count++; 682204e1e74SAdriana Kobylak // Don't put the functional version on the queue since we can't 683204e1e74SAdriana Kobylak // remove the "running" BMC version. 6840f88b5afSLei YU // If ACTIVE_BMC_MAX_ALLOWED <= 1, there is only one active BMC, 6850f88b5afSLei YU // so remove functional version as well. 686a6963590SAdriana Kobylak // Don't delete the the Activation object that called this function. 687a6963590SAdriana Kobylak if ((versions.find(iter.second->versionId) 688a6963590SAdriana Kobylak ->second->isFunctional() && 689a6963590SAdriana Kobylak ACTIVE_BMC_MAX_ALLOWED > 1) || 690a6963590SAdriana Kobylak (iter.second->versionId == caller.versionId)) 691204e1e74SAdriana Kobylak { 692204e1e74SAdriana Kobylak continue; 693204e1e74SAdriana Kobylak } 694a6963590SAdriana Kobylak 695a6963590SAdriana Kobylak // Failed activations don't have priority, assign them a large value 696a6963590SAdriana Kobylak // for sorting purposes. 697a6963590SAdriana Kobylak auto priority = 999; 698a6963590SAdriana Kobylak if (iter.second.get()->activation() == 699a6963590SAdriana Kobylak server::Activation::Activations::Active) 700a6963590SAdriana Kobylak { 701a6963590SAdriana Kobylak priority = iter.second->redundancyPriority.get()->priority(); 702a6963590SAdriana Kobylak } 703a6963590SAdriana Kobylak 704a6963590SAdriana Kobylak versionsPQ.push(std::make_pair(priority, iter.second->versionId)); 705204e1e74SAdriana Kobylak } 706204e1e74SAdriana Kobylak } 707204e1e74SAdriana Kobylak 708204e1e74SAdriana Kobylak // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1, 709204e1e74SAdriana Kobylak // remove the highest priority one(s). 710204e1e74SAdriana Kobylak while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty())) 711204e1e74SAdriana Kobylak { 712204e1e74SAdriana Kobylak erase(versionsPQ.top().second); 713204e1e74SAdriana Kobylak versionsPQ.pop(); 714204e1e74SAdriana Kobylak count--; 715204e1e74SAdriana Kobylak } 716204e1e74SAdriana Kobylak } 717204e1e74SAdriana Kobylak 718eaa1ee05SEddie James void ItemUpdater::mirrorUbootToAlt() 719eaa1ee05SEddie James { 72056aaf454SLei YU helper.mirrorAlt(); 721eaa1ee05SEddie James } 722eaa1ee05SEddie James 723ec1b41c4SGunnar Mills } // namespace updater 724ec1b41c4SGunnar Mills } // namespace software 725ec1b41c4SGunnar Mills } // namespace phosphor 726