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 10b0ce996aSGunnar Mills #include <experimental/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> 18d5b8f75cSAdriana Kobylak #include <xyz/openbmc_project/Common/error.hpp> 1943699ca7SAdriana Kobylak #include <xyz/openbmc_project/Software/Image/error.hpp> 20ec1b41c4SGunnar Mills 21ec1b41c4SGunnar Mills namespace phosphor 22ec1b41c4SGunnar Mills { 23ec1b41c4SGunnar Mills namespace software 24ec1b41c4SGunnar Mills { 25ec1b41c4SGunnar Mills namespace updater 26ec1b41c4SGunnar Mills { 27ec1b41c4SGunnar Mills 282ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class 292ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server; 300129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server; 312ce7da29SGunnar Mills 322ce7da29SGunnar Mills using namespace phosphor::logging; 3343699ca7SAdriana Kobylak using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error; 342ab9b109SJayanth Othayoth using namespace phosphor::software::image; 3535e83f3eSSaqib Khan namespace fs = std::experimental::filesystem; 36d5b8f75cSAdriana Kobylak using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 3735e83f3eSSaqib Khan 38e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg) 39ec1b41c4SGunnar Mills { 4084a0e693SSaqib Khan 4184a0e693SSaqib Khan using SVersion = server::Version; 4284a0e693SSaqib Khan using VersionPurpose = SVersion::VersionPurpose; 439a782243SGunnar Mills using VersionClass = phosphor::software::manager::Version; 4484a0e693SSaqib Khan namespace mesg = sdbusplus::message; 4584a0e693SSaqib Khan namespace variant_ns = mesg::variant_ns; 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( 6584a0e693SSaqib Khan variant_ns::get<std::string>(property.second)); 6684a0e693SSaqib Khan if (value == VersionPurpose::BMC || 6784a0e693SSaqib Khan value == VersionPurpose::System) 6884a0e693SSaqib Khan { 6984a0e693SSaqib Khan purpose = value; 7084a0e693SSaqib Khan } 71705f1bfcSSaqib Khan } 72705f1bfcSSaqib Khan else if (property.first == "Version") 73705f1bfcSSaqib Khan { 7484a0e693SSaqib Khan version = variant_ns::get<std::string>(property.second); 75705f1bfcSSaqib Khan } 76705f1bfcSSaqib Khan } 77705f1bfcSSaqib Khan } 7819177d3eSSaqib Khan else if (intf.first == FILEPATH_IFACE) 7919177d3eSSaqib Khan { 8019177d3eSSaqib Khan for (const auto& property : intf.second) 8119177d3eSSaqib Khan { 8219177d3eSSaqib Khan if (property.first == "Path") 8319177d3eSSaqib Khan { 8484a0e693SSaqib Khan filePath = variant_ns::get<std::string>(property.second); 8519177d3eSSaqib Khan } 8619177d3eSSaqib Khan } 8719177d3eSSaqib Khan } 88705f1bfcSSaqib Khan } 892285fe0fSAdriana Kobylak if (version.empty() || filePath.empty() || 9084a0e693SSaqib Khan purpose == VersionPurpose::Unknown) 912ce7da29SGunnar Mills { 92e75d10f5SPatrick Williams return; 932ce7da29SGunnar Mills } 942ce7da29SGunnar Mills 952ce7da29SGunnar Mills // Version id is the last item in the path 962ce7da29SGunnar Mills auto pos = path.rfind("/"); 972ce7da29SGunnar Mills if (pos == std::string::npos) 982ce7da29SGunnar Mills { 992ce7da29SGunnar Mills log<level::ERR>("No version id found in object path", 100596466b8SAdriana Kobylak entry("OBJPATH=%s", path.c_str())); 101e75d10f5SPatrick Williams return; 1022ce7da29SGunnar Mills } 1032ce7da29SGunnar Mills 1042ce7da29SGunnar Mills auto versionId = path.substr(pos + 1); 1052ce7da29SGunnar Mills 106e75d10f5SPatrick Williams if (activations.find(versionId) == activations.end()) 1072ce7da29SGunnar Mills { 10835e83f3eSSaqib Khan // Determine the Activation state by processing the given image dir. 10935e83f3eSSaqib Khan auto activationState = server::Activation::Activations::Invalid; 1109a782243SGunnar Mills ItemUpdater::ActivationStatus result = 1119a782243SGunnar Mills ItemUpdater::validateSquashFSImage(filePath); 11243b25cdeSGunnar Mills AssociationList associations = {}; 11343b25cdeSGunnar Mills 11435e83f3eSSaqib Khan if (result == ItemUpdater::ActivationStatus::ready) 11535e83f3eSSaqib Khan { 11635e83f3eSSaqib Khan activationState = server::Activation::Activations::Ready; 117b60add1eSGunnar Mills // Create an association to the BMC inventory item 1182285fe0fSAdriana Kobylak associations.emplace_back( 1192285fe0fSAdriana Kobylak std::make_tuple(ACTIVATION_FWD_ASSOCIATION, 1202285fe0fSAdriana Kobylak ACTIVATION_REV_ASSOCIATION, bmcInventoryPath)); 12143b25cdeSGunnar Mills } 122b60add1eSGunnar Mills 123ee13e831SSaqib Khan activations.insert(std::make_pair( 124ee13e831SSaqib Khan versionId, 1252285fe0fSAdriana Kobylak std::make_unique<Activation>(bus, path, *this, versionId, 1262285fe0fSAdriana Kobylak activationState, associations))); 1274254beceSMichael Tritz 128ee13e831SSaqib Khan auto versionPtr = std::make_unique<VersionClass>( 1292285fe0fSAdriana Kobylak bus, path, version, purpose, filePath, 1302285fe0fSAdriana Kobylak std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); 131ee13e831SSaqib Khan versionPtr->deleteObject = 1322285fe0fSAdriana Kobylak std::make_unique<phosphor::software::manager::Delete>(bus, path, 1332285fe0fSAdriana Kobylak *versionPtr); 134ee13e831SSaqib Khan versions.insert(std::make_pair(versionId, std::move(versionPtr))); 1352ce7da29SGunnar Mills } 136e75d10f5SPatrick Williams return; 137ec1b41c4SGunnar Mills } 138ec1b41c4SGunnar Mills 139ba239881SSaqib Khan void ItemUpdater::processBMCImage() 140ba239881SSaqib Khan { 14188e8a325SGunnar Mills using VersionClass = phosphor::software::manager::Version; 142269bff30SLei YU 143269bff30SLei YU // Check MEDIA_DIR and create if it does not exist 144269bff30SLei YU try 145269bff30SLei YU { 146269bff30SLei YU if (!fs::is_directory(MEDIA_DIR)) 147269bff30SLei YU { 148269bff30SLei YU fs::create_directory(MEDIA_DIR); 149269bff30SLei YU } 150269bff30SLei YU } 151269bff30SLei YU catch (const fs::filesystem_error& e) 152269bff30SLei YU { 153269bff30SLei YU log<level::ERR>("Failed to prepare dir", entry("ERR=%s", e.what())); 154269bff30SLei YU return; 155269bff30SLei YU } 156269bff30SLei YU 15788e8a325SGunnar Mills // Read os-release from /etc/ to get the functional BMC version 15888e8a325SGunnar Mills auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE); 15988e8a325SGunnar Mills 1601eef62deSSaqib Khan // Read os-release from folders under /media/ to get 1611eef62deSSaqib Khan // BMC Software Versions. 1621eef62deSSaqib Khan for (const auto& iter : fs::directory_iterator(MEDIA_DIR)) 1631eef62deSSaqib Khan { 1641eef62deSSaqib Khan auto activationState = server::Activation::Activations::Active; 1656fab70daSSaqib Khan static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX); 1661eef62deSSaqib Khan 1671eef62deSSaqib Khan // Check if the BMC_RO_PREFIXis the prefix of the iter.path 1682285fe0fSAdriana Kobylak if (0 == 1692285fe0fSAdriana Kobylak iter.path().native().compare(0, BMC_RO_PREFIX_LEN, BMC_ROFS_PREFIX)) 1701eef62deSSaqib Khan { 171021c365bSSaqib Khan // The versionId is extracted from the path 172021c365bSSaqib Khan // for example /media/ro-2a1022fe. 173021c365bSSaqib Khan auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN); 1741eef62deSSaqib Khan auto osRelease = iter.path() / OS_RELEASE_FILE; 1751eef62deSSaqib Khan if (!fs::is_regular_file(osRelease)) 1761eef62deSSaqib Khan { 1772285fe0fSAdriana Kobylak log<level::ERR>( 1782285fe0fSAdriana Kobylak "Failed to read osRelease", 179596466b8SAdriana Kobylak entry("FILENAME=%s", osRelease.string().c_str())); 180021c365bSSaqib Khan ItemUpdater::erase(id); 181021c365bSSaqib Khan continue; 1821eef62deSSaqib Khan } 18388e8a325SGunnar Mills auto version = VersionClass::getBMCVersion(osRelease); 1841eef62deSSaqib Khan if (version.empty()) 1851eef62deSSaqib Khan { 1862285fe0fSAdriana Kobylak log<level::ERR>( 1872285fe0fSAdriana Kobylak "Failed to read version from osRelease", 188596466b8SAdriana Kobylak entry("FILENAME=%s", osRelease.string().c_str())); 1891eef62deSSaqib Khan activationState = server::Activation::Activations::Invalid; 1901eef62deSSaqib Khan } 191021c365bSSaqib Khan 1921eef62deSSaqib Khan auto purpose = server::Version::VersionPurpose::BMC; 1931eef62deSSaqib Khan auto path = fs::path(SOFTWARE_OBJPATH) / id; 1941eef62deSSaqib Khan 195269bff30SLei YU // Create functional association if this is the functional 196269bff30SLei YU // version 19788e8a325SGunnar Mills if (version.compare(functionalVersion) == 0) 19888e8a325SGunnar Mills { 19988e8a325SGunnar Mills createFunctionalAssociation(path); 20088e8a325SGunnar Mills } 20188e8a325SGunnar Mills 20243b25cdeSGunnar Mills AssociationList associations = {}; 20343b25cdeSGunnar Mills 20443b25cdeSGunnar Mills if (activationState == server::Activation::Activations::Active) 20543b25cdeSGunnar Mills { 20643b25cdeSGunnar Mills // Create an association to the BMC inventory item 20743b25cdeSGunnar Mills associations.emplace_back(std::make_tuple( 2082285fe0fSAdriana Kobylak ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION, 20943b25cdeSGunnar Mills bmcInventoryPath)); 21043b25cdeSGunnar Mills 21143b25cdeSGunnar Mills // Create an active association since this image is active 21243b25cdeSGunnar Mills createActiveAssociation(path); 21343b25cdeSGunnar Mills } 21443b25cdeSGunnar Mills 215ee590c74SAdriana Kobylak // Create Version instance for this version. 216ee590c74SAdriana Kobylak auto versionPtr = std::make_unique<VersionClass>( 2172285fe0fSAdriana Kobylak bus, path, version, purpose, "", 2182285fe0fSAdriana Kobylak std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); 219ee590c74SAdriana Kobylak auto isVersionFunctional = versionPtr->isFunctional(); 220ee13e831SSaqib Khan if (!isVersionFunctional) 221ee13e831SSaqib Khan { 222ee13e831SSaqib Khan versionPtr->deleteObject = 223ee13e831SSaqib Khan std::make_unique<phosphor::software::manager::Delete>( 224ee13e831SSaqib Khan bus, path, *versionPtr); 225ee13e831SSaqib Khan } 2262285fe0fSAdriana Kobylak versions.insert(std::make_pair(id, std::move(versionPtr))); 227ee590c74SAdriana Kobylak 2281eef62deSSaqib Khan // Create Activation instance for this version. 229ee13e831SSaqib Khan activations.insert(std::make_pair( 2302285fe0fSAdriana Kobylak id, std::make_unique<Activation>( 2312285fe0fSAdriana Kobylak bus, path, *this, id, activationState, associations))); 2321eef62deSSaqib Khan 233269bff30SLei YU // If Active, create RedundancyPriority instance for this 234269bff30SLei YU // version. 2351eef62deSSaqib Khan if (activationState == server::Activation::Activations::Active) 2361eef62deSSaqib Khan { 2371eef62deSSaqib Khan uint8_t priority = std::numeric_limits<uint8_t>::max(); 238*687e75e2SAdriana Kobylak if (!restorePriority(id, priority)) 2391eef62deSSaqib Khan { 240ee590c74SAdriana Kobylak if (isVersionFunctional) 241ee590c74SAdriana Kobylak { 242ee590c74SAdriana Kobylak priority = 0; 243ee590c74SAdriana Kobylak } 244ee590c74SAdriana Kobylak else 245ee590c74SAdriana Kobylak { 2461eef62deSSaqib Khan log<level::ERR>("Unable to restore priority from file.", 247596466b8SAdriana Kobylak entry("VERSIONID=%s", id.c_str())); 2481eef62deSSaqib Khan } 249ee590c74SAdriana Kobylak } 2501eef62deSSaqib Khan activations.find(id)->second->redundancyPriority = 2511eef62deSSaqib Khan std::make_unique<RedundancyPriority>( 2522285fe0fSAdriana Kobylak bus, path, *(activations.find(id)->second), priority, 253b77551cdSAdriana Kobylak false); 2541eef62deSSaqib Khan } 2551eef62deSSaqib Khan } 2561eef62deSSaqib Khan } 257dcbfa04aSSaqib Khan 258dcbfa04aSSaqib Khan // If there is no ubi volume for bmc version then read the /etc/os-release 259dcbfa04aSSaqib Khan // and create rofs-<versionId> under /media 260dcbfa04aSSaqib Khan if (activations.size() == 0) 261dcbfa04aSSaqib Khan { 262d16bcbd5SGunnar Mills auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE); 263dcbfa04aSSaqib Khan auto id = phosphor::software::manager::Version::getId(version); 264dcbfa04aSSaqib Khan auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/"; 265dcbfa04aSSaqib Khan try 266dcbfa04aSSaqib Khan { 267dcbfa04aSSaqib Khan if (!fs::is_directory(versionFileDir)) 268dcbfa04aSSaqib Khan { 269dcbfa04aSSaqib Khan fs::create_directories(versionFileDir); 270dcbfa04aSSaqib Khan } 271dcbfa04aSSaqib Khan auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE; 272dcbfa04aSSaqib Khan fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath); 273dcbfa04aSSaqib Khan ItemUpdater::processBMCImage(); 274dcbfa04aSSaqib Khan } 275dcbfa04aSSaqib Khan catch (const std::exception& e) 276dcbfa04aSSaqib Khan { 277dcbfa04aSSaqib Khan log<level::ERR>(e.what()); 278dcbfa04aSSaqib Khan } 279dcbfa04aSSaqib Khan } 280eaa1ee05SEddie James 281eaa1ee05SEddie James mirrorUbootToAlt(); 282ba239881SSaqib Khan return; 283ba239881SSaqib Khan } 284ba239881SSaqib Khan 2853526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId) 2863526ef73SLeonel Gonzalez { 2876d873715SEddie James // Find entry in versions map 2886d873715SEddie James auto it = versions.find(entryId); 2896d873715SEddie James if (it != versions.end()) 2906d873715SEddie James { 2910f88b5afSLei YU if (it->second->isFunctional() && ACTIVE_BMC_MAX_ALLOWED > 1) 2926d873715SEddie James { 2932285fe0fSAdriana Kobylak log<level::ERR>("Error: Version is currently running on the BMC. " 2942285fe0fSAdriana Kobylak "Unable to remove.", 2952285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 2966d873715SEddie James return; 2976d873715SEddie James } 2986d873715SEddie James 2996d873715SEddie James // Delete ReadOnly partitions if it's not active 3003526ef73SLeonel Gonzalez removeReadOnlyPartition(entryId); 301*687e75e2SAdriana Kobylak removePersistDataDirectory(entryId); 302ee13e831SSaqib Khan 303ee13e831SSaqib Khan // Removing entry in versions map 304ee13e831SSaqib Khan this->versions.erase(entryId); 3056d873715SEddie James } 3066d873715SEddie James else 3076d873715SEddie James { 3086d873715SEddie James // Delete ReadOnly partitions even if we can't find the version 3096d873715SEddie James removeReadOnlyPartition(entryId); 310*687e75e2SAdriana Kobylak removePersistDataDirectory(entryId); 3116d873715SEddie James 3122285fe0fSAdriana Kobylak log<level::ERR>("Error: Failed to find version in item updater " 3132285fe0fSAdriana Kobylak "versions map. Unable to remove.", 3142285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 3156d873715SEddie James } 3161eef62deSSaqib Khan 31756aaf454SLei YU helper.clearEntry(entryId); 3183526ef73SLeonel Gonzalez 3193526ef73SLeonel Gonzalez // Removing entry in activations map 3203526ef73SLeonel Gonzalez auto ita = activations.find(entryId); 3213526ef73SLeonel Gonzalez if (ita == activations.end()) 3223526ef73SLeonel Gonzalez { 3232285fe0fSAdriana Kobylak log<level::ERR>("Error: Failed to find version in item updater " 3242285fe0fSAdriana Kobylak "activations map. Unable to remove.", 3252285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 3263526ef73SLeonel Gonzalez } 327ee13e831SSaqib Khan else 328ee13e831SSaqib Khan { 329991af7ecSAdriana Kobylak removeAssociations(ita->second->path); 3303526ef73SLeonel Gonzalez this->activations.erase(entryId); 331ee13e831SSaqib Khan } 33249446ae9SSaqib Khan ItemUpdater::resetUbootEnvVars(); 333ee13e831SSaqib Khan return; 3343526ef73SLeonel Gonzalez } 3353526ef73SLeonel Gonzalez 336bc1bf3afSMichael Tritz void ItemUpdater::deleteAll() 337bc1bf3afSMichael Tritz { 33883cd21fbSAdriana Kobylak std::vector<std::string> deletableVersions; 33983cd21fbSAdriana Kobylak 340bc1bf3afSMichael Tritz for (const auto& versionIt : versions) 341bc1bf3afSMichael Tritz { 342bc1bf3afSMichael Tritz if (!versionIt.second->isFunctional()) 343bc1bf3afSMichael Tritz { 34483cd21fbSAdriana Kobylak deletableVersions.push_back(versionIt.first); 345bc1bf3afSMichael Tritz } 346bc1bf3afSMichael Tritz } 347bc1bf3afSMichael Tritz 34883cd21fbSAdriana Kobylak for (const auto& deletableIt : deletableVersions) 34983cd21fbSAdriana Kobylak { 35083cd21fbSAdriana Kobylak ItemUpdater::erase(deletableIt); 35183cd21fbSAdriana Kobylak } 35283cd21fbSAdriana Kobylak 35356aaf454SLei YU helper.cleanup(); 354bc1bf3afSMichael Tritz } 355bc1bf3afSMichael Tritz 3562285fe0fSAdriana Kobylak ItemUpdater::ActivationStatus 3572285fe0fSAdriana Kobylak ItemUpdater::validateSquashFSImage(const std::string& filePath) 35835e83f3eSSaqib Khan { 359b1cfdf99SMichael Tritz bool invalid = false; 36035e83f3eSSaqib Khan 361b1cfdf99SMichael Tritz for (auto& bmcImage : bmcImages) 362b1cfdf99SMichael Tritz { 36319177d3eSSaqib Khan fs::path file(filePath); 36435e83f3eSSaqib Khan file /= bmcImage; 36535e83f3eSSaqib Khan std::ifstream efile(file.c_str()); 366b1cfdf99SMichael Tritz if (efile.good() != 1) 36735e83f3eSSaqib Khan { 368b1cfdf99SMichael Tritz log<level::ERR>("Failed to find the BMC image.", 369b1cfdf99SMichael Tritz entry("IMAGE=%s", bmcImage.c_str())); 370b1cfdf99SMichael Tritz invalid = true; 37135e83f3eSSaqib Khan } 372b1cfdf99SMichael Tritz } 373b1cfdf99SMichael Tritz 374b1cfdf99SMichael Tritz if (invalid) 37535e83f3eSSaqib Khan { 37635e83f3eSSaqib Khan return ItemUpdater::ActivationStatus::invalid; 37735e83f3eSSaqib Khan } 378b1cfdf99SMichael Tritz 379b1cfdf99SMichael Tritz return ItemUpdater::ActivationStatus::ready; 38035e83f3eSSaqib Khan } 38135e83f3eSSaqib Khan 382bbcb7be1SAdriana Kobylak void ItemUpdater::savePriority(const std::string& versionId, uint8_t value) 383bbcb7be1SAdriana Kobylak { 384*687e75e2SAdriana Kobylak storePriority(versionId, value); 385bbcb7be1SAdriana Kobylak helper.setEntry(versionId, value); 386bbcb7be1SAdriana Kobylak } 387bbcb7be1SAdriana Kobylak 388b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId) 3894c1aec09SSaqib Khan { 390b77551cdSAdriana Kobylak std::map<std::string, uint8_t> priorityMap; 391b77551cdSAdriana Kobylak 392b77551cdSAdriana Kobylak // Insert the requested version and priority, it may not exist yet. 393b77551cdSAdriana Kobylak priorityMap.insert(std::make_pair(versionId, value)); 394b77551cdSAdriana Kobylak 3954c1aec09SSaqib Khan for (const auto& intf : activations) 3964c1aec09SSaqib Khan { 3974c1aec09SSaqib Khan if (intf.second->redundancyPriority) 3984c1aec09SSaqib Khan { 399b77551cdSAdriana Kobylak priorityMap.insert(std::make_pair( 4002285fe0fSAdriana Kobylak intf.first, intf.second->redundancyPriority.get()->priority())); 401b77551cdSAdriana Kobylak } 402b77551cdSAdriana Kobylak } 403b77551cdSAdriana Kobylak 404b77551cdSAdriana Kobylak // Lambda function to compare 2 priority values, use <= to allow duplicates 4052285fe0fSAdriana Kobylak typedef std::function<bool(std::pair<std::string, uint8_t>, 4062285fe0fSAdriana Kobylak std::pair<std::string, uint8_t>)> 4072285fe0fSAdriana Kobylak cmpPriority; 4082285fe0fSAdriana Kobylak cmpPriority cmpPriorityFunc = 4092285fe0fSAdriana Kobylak [](std::pair<std::string, uint8_t> priority1, 4102285fe0fSAdriana Kobylak std::pair<std::string, uint8_t> priority2) { 411b77551cdSAdriana Kobylak return priority1.second <= priority2.second; 412b77551cdSAdriana Kobylak }; 413b77551cdSAdriana Kobylak 414b77551cdSAdriana Kobylak // Sort versions by ascending priority 415b77551cdSAdriana Kobylak std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet( 416b77551cdSAdriana Kobylak priorityMap.begin(), priorityMap.end(), cmpPriorityFunc); 417b77551cdSAdriana Kobylak 418b77551cdSAdriana Kobylak auto freePriorityValue = value; 419b77551cdSAdriana Kobylak for (auto& element : prioritySet) 420b77551cdSAdriana Kobylak { 421b77551cdSAdriana Kobylak if (element.first == versionId) 422b77551cdSAdriana Kobylak { 423b77551cdSAdriana Kobylak continue; 424b77551cdSAdriana Kobylak } 425b77551cdSAdriana Kobylak if (element.second == freePriorityValue) 426b77551cdSAdriana Kobylak { 427b77551cdSAdriana Kobylak ++freePriorityValue; 428b77551cdSAdriana Kobylak auto it = activations.find(element.first); 429b77551cdSAdriana Kobylak it->second->redundancyPriority.get()->sdbusPriority( 430b77551cdSAdriana Kobylak freePriorityValue); 4314c1aec09SSaqib Khan } 4324c1aec09SSaqib Khan } 433b77551cdSAdriana Kobylak 434b77551cdSAdriana Kobylak auto lowestVersion = prioritySet.begin()->first; 435b77551cdSAdriana Kobylak if (value == prioritySet.begin()->second) 436b77551cdSAdriana Kobylak { 437b77551cdSAdriana Kobylak lowestVersion = versionId; 4384c1aec09SSaqib Khan } 439b77551cdSAdriana Kobylak updateUbootEnvVars(lowestVersion); 4404c1aec09SSaqib Khan } 4414c1aec09SSaqib Khan 44237a59043SMichael Tritz void ItemUpdater::reset() 44337a59043SMichael Tritz { 44456aaf454SLei YU helper.factoryReset(); 44537a59043SMichael Tritz 44637a59043SMichael Tritz log<level::INFO>("BMC factory reset will take effect upon reboot."); 44737a59043SMichael Tritz } 44837a59043SMichael Tritz 4493526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId) 4503526ef73SLeonel Gonzalez { 45156aaf454SLei YU helper.removeVersion(versionId); 4523526ef73SLeonel Gonzalez } 4533526ef73SLeonel Gonzalez 4540129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value) 4550129d926SMichael Tritz { 4560129d926SMichael Tritz // enabling field mode is intended to be one way: false -> true 4570129d926SMichael Tritz if (value && !control::FieldMode::fieldModeEnabled()) 4580129d926SMichael Tritz { 4590129d926SMichael Tritz control::FieldMode::fieldModeEnabled(value); 4600129d926SMichael Tritz 46122848eceSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 46222848eceSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 46322848eceSAdriana Kobylak method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service", 46422848eceSAdriana Kobylak "replace"); 46522848eceSAdriana Kobylak bus.call_noreply(method); 46622848eceSAdriana Kobylak 46722848eceSAdriana Kobylak method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 46822848eceSAdriana Kobylak SYSTEMD_INTERFACE, "StopUnit"); 46922848eceSAdriana Kobylak method.append("usr-local.mount", "replace"); 47022848eceSAdriana Kobylak bus.call_noreply(method); 47122848eceSAdriana Kobylak 47222848eceSAdriana Kobylak std::vector<std::string> usrLocal = {"usr-local.mount"}; 47322848eceSAdriana Kobylak 47422848eceSAdriana Kobylak method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 47522848eceSAdriana Kobylak SYSTEMD_INTERFACE, "MaskUnitFiles"); 47622848eceSAdriana Kobylak method.append(usrLocal, false, true); 47722848eceSAdriana Kobylak bus.call_noreply(method); 4780129d926SMichael Tritz } 479d5b8f75cSAdriana Kobylak else if (!value && control::FieldMode::fieldModeEnabled()) 480d5b8f75cSAdriana Kobylak { 481d5b8f75cSAdriana Kobylak elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON( 482d5b8f75cSAdriana Kobylak "FieldMode is not allowed to be cleared")); 483d5b8f75cSAdriana Kobylak } 4840129d926SMichael Tritz 4850129d926SMichael Tritz return control::FieldMode::fieldModeEnabled(); 4860129d926SMichael Tritz } 4870129d926SMichael Tritz 4880129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus() 4890129d926SMichael Tritz { 490ff0b421dSMichael Tritz std::ifstream input("/dev/mtd/u-boot-env"); 4910129d926SMichael Tritz std::string envVar; 4920129d926SMichael Tritz std::getline(input, envVar); 4930129d926SMichael Tritz 4940129d926SMichael Tritz if (envVar.find("fieldmode=true") != std::string::npos) 4950129d926SMichael Tritz { 4960129d926SMichael Tritz ItemUpdater::fieldModeEnabled(true); 4970129d926SMichael Tritz } 4980129d926SMichael Tritz } 4990129d926SMichael Tritz 500b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath() 501b60add1eSGunnar Mills { 502b60add1eSGunnar Mills auto depth = 0; 5032285fe0fSAdriana Kobylak auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 5042285fe0fSAdriana Kobylak MAPPER_INTERFACE, "GetSubTreePaths"); 505b60add1eSGunnar Mills 5061254c628SAdriana Kobylak mapperCall.append(INVENTORY_PATH); 507b60add1eSGunnar Mills mapperCall.append(depth); 5081254c628SAdriana Kobylak std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE}; 509b60add1eSGunnar Mills mapperCall.append(filter); 510b60add1eSGunnar Mills 51187c78173SEd Tanous try 512b60add1eSGunnar Mills { 51387c78173SEd Tanous auto response = bus.call(mapperCall); 514b60add1eSGunnar Mills 515b60add1eSGunnar Mills using ObjectPaths = std::vector<std::string>; 516b60add1eSGunnar Mills ObjectPaths result; 517b60add1eSGunnar Mills response.read(result); 518b60add1eSGunnar Mills 5191254c628SAdriana Kobylak if (!result.empty()) 520b60add1eSGunnar Mills { 5211254c628SAdriana Kobylak bmcInventoryPath = result.front(); 522b60add1eSGunnar Mills } 52387c78173SEd Tanous } 52487c78173SEd Tanous catch (const sdbusplus::exception::SdBusError& e) 52587c78173SEd Tanous { 52687c78173SEd Tanous log<level::ERR>("Error in mapper GetSubTreePath"); 52787c78173SEd Tanous return; 52887c78173SEd Tanous } 529b60add1eSGunnar Mills 530b60add1eSGunnar Mills return; 531b60add1eSGunnar Mills } 532b60add1eSGunnar Mills 533f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path) 534ded875dcSGunnar Mills { 5352285fe0fSAdriana Kobylak assocs.emplace_back( 5362285fe0fSAdriana Kobylak std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path)); 537ded875dcSGunnar Mills associations(assocs); 538ded875dcSGunnar Mills } 539ded875dcSGunnar Mills 54088e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path) 54188e8a325SGunnar Mills { 54288e8a325SGunnar Mills assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION, 5432285fe0fSAdriana Kobylak FUNCTIONAL_REV_ASSOCIATION, path)); 54488e8a325SGunnar Mills associations(assocs); 54588e8a325SGunnar Mills } 54688e8a325SGunnar Mills 547991af7ecSAdriana Kobylak void ItemUpdater::removeAssociations(const std::string& path) 548ded875dcSGunnar Mills { 549ded875dcSGunnar Mills for (auto iter = assocs.begin(); iter != assocs.end();) 550ded875dcSGunnar Mills { 551991af7ecSAdriana Kobylak if ((std::get<2>(*iter)).compare(path) == 0) 552ded875dcSGunnar Mills { 553ded875dcSGunnar Mills iter = assocs.erase(iter); 554ded875dcSGunnar Mills associations(assocs); 555ded875dcSGunnar Mills } 556ded875dcSGunnar Mills else 557ded875dcSGunnar Mills { 558ded875dcSGunnar Mills ++iter; 559ded875dcSGunnar Mills } 560ded875dcSGunnar Mills } 561ded875dcSGunnar Mills } 562ded875dcSGunnar Mills 563b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value) 564b9da6634SSaqib Khan { 565b9da6634SSaqib Khan for (const auto& intf : activations) 566b9da6634SSaqib Khan { 567b9da6634SSaqib Khan if (intf.second->redundancyPriority) 568b9da6634SSaqib Khan { 569b9da6634SSaqib Khan if (intf.second->redundancyPriority.get()->priority() < value) 570b9da6634SSaqib Khan { 571b9da6634SSaqib Khan return false; 572b9da6634SSaqib Khan } 573b9da6634SSaqib Khan } 574b9da6634SSaqib Khan } 575b9da6634SSaqib Khan return true; 576b9da6634SSaqib Khan } 577b9da6634SSaqib Khan 578b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId) 579b77551cdSAdriana Kobylak { 58056aaf454SLei YU helper.updateUbootVersionId(versionId); 581b77551cdSAdriana Kobylak } 582b77551cdSAdriana Kobylak 58349446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars() 58449446ae9SSaqib Khan { 58549446ae9SSaqib Khan decltype(activations.begin()->second->redundancyPriority.get()->priority()) 58649446ae9SSaqib Khan lowestPriority = std::numeric_limits<uint8_t>::max(); 58749446ae9SSaqib Khan decltype(activations.begin()->second->versionId) lowestPriorityVersion; 58849446ae9SSaqib Khan for (const auto& intf : activations) 58949446ae9SSaqib Khan { 59049446ae9SSaqib Khan if (!intf.second->redundancyPriority.get()) 59149446ae9SSaqib Khan { 59249446ae9SSaqib Khan // Skip this version if the redundancyPriority is not initialized. 59349446ae9SSaqib Khan continue; 59449446ae9SSaqib Khan } 59549446ae9SSaqib Khan 5962285fe0fSAdriana Kobylak if (intf.second->redundancyPriority.get()->priority() <= lowestPriority) 59749446ae9SSaqib Khan { 59849446ae9SSaqib Khan lowestPriority = intf.second->redundancyPriority.get()->priority(); 59949446ae9SSaqib Khan lowestPriorityVersion = intf.second->versionId; 60049446ae9SSaqib Khan } 60149446ae9SSaqib Khan } 60249446ae9SSaqib Khan 603f0382c35SSaqib Khan // Update the U-boot environment variable to point to the lowest priority 604b77551cdSAdriana Kobylak updateUbootEnvVars(lowestPriorityVersion); 60549446ae9SSaqib Khan } 60649446ae9SSaqib Khan 607a6963590SAdriana Kobylak void ItemUpdater::freeSpace(Activation& caller) 608204e1e74SAdriana Kobylak { 609204e1e74SAdriana Kobylak // Versions with the highest priority in front 610204e1e74SAdriana Kobylak std::priority_queue<std::pair<int, std::string>, 611204e1e74SAdriana Kobylak std::vector<std::pair<int, std::string>>, 6122285fe0fSAdriana Kobylak std::less<std::pair<int, std::string>>> 6132285fe0fSAdriana Kobylak versionsPQ; 614204e1e74SAdriana Kobylak 615204e1e74SAdriana Kobylak std::size_t count = 0; 616204e1e74SAdriana Kobylak for (const auto& iter : activations) 617204e1e74SAdriana Kobylak { 618204e1e74SAdriana Kobylak if ((iter.second.get()->activation() == 619204e1e74SAdriana Kobylak server::Activation::Activations::Active) || 620204e1e74SAdriana Kobylak (iter.second.get()->activation() == 621204e1e74SAdriana Kobylak server::Activation::Activations::Failed)) 622204e1e74SAdriana Kobylak { 623204e1e74SAdriana Kobylak count++; 624204e1e74SAdriana Kobylak // Don't put the functional version on the queue since we can't 625204e1e74SAdriana Kobylak // remove the "running" BMC version. 6260f88b5afSLei YU // If ACTIVE_BMC_MAX_ALLOWED <= 1, there is only one active BMC, 6270f88b5afSLei YU // so remove functional version as well. 628a6963590SAdriana Kobylak // Don't delete the the Activation object that called this function. 629a6963590SAdriana Kobylak if ((versions.find(iter.second->versionId) 630a6963590SAdriana Kobylak ->second->isFunctional() && 631a6963590SAdriana Kobylak ACTIVE_BMC_MAX_ALLOWED > 1) || 632a6963590SAdriana Kobylak (iter.second->versionId == caller.versionId)) 633204e1e74SAdriana Kobylak { 634204e1e74SAdriana Kobylak continue; 635204e1e74SAdriana Kobylak } 636a6963590SAdriana Kobylak 637a6963590SAdriana Kobylak // Failed activations don't have priority, assign them a large value 638a6963590SAdriana Kobylak // for sorting purposes. 639a6963590SAdriana Kobylak auto priority = 999; 640a6963590SAdriana Kobylak if (iter.second.get()->activation() == 641a6963590SAdriana Kobylak server::Activation::Activations::Active) 642a6963590SAdriana Kobylak { 643a6963590SAdriana Kobylak priority = iter.second->redundancyPriority.get()->priority(); 644a6963590SAdriana Kobylak } 645a6963590SAdriana Kobylak 646a6963590SAdriana Kobylak versionsPQ.push(std::make_pair(priority, iter.second->versionId)); 647204e1e74SAdriana Kobylak } 648204e1e74SAdriana Kobylak } 649204e1e74SAdriana Kobylak 650204e1e74SAdriana Kobylak // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1, 651204e1e74SAdriana Kobylak // remove the highest priority one(s). 652204e1e74SAdriana Kobylak while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty())) 653204e1e74SAdriana Kobylak { 654204e1e74SAdriana Kobylak erase(versionsPQ.top().second); 655204e1e74SAdriana Kobylak versionsPQ.pop(); 656204e1e74SAdriana Kobylak count--; 657204e1e74SAdriana Kobylak } 658204e1e74SAdriana Kobylak } 659204e1e74SAdriana Kobylak 660eaa1ee05SEddie James void ItemUpdater::mirrorUbootToAlt() 661eaa1ee05SEddie James { 66256aaf454SLei YU helper.mirrorAlt(); 663eaa1ee05SEddie James } 664eaa1ee05SEddie James 665ec1b41c4SGunnar Mills } // namespace updater 666ec1b41c4SGunnar Mills } // namespace software 667ec1b41c4SGunnar Mills } // namespace phosphor 668