135e83f3eSSaqib Khan #include <fstream> 2204e1e74SAdriana Kobylak #include <queue> 3b77551cdSAdriana Kobylak #include <set> 4ec1b41c4SGunnar Mills #include <string> 52ce7da29SGunnar Mills #include <phosphor-logging/log.hpp> 6dcbfa04aSSaqib Khan #include <phosphor-logging/elog.hpp> 7dcbfa04aSSaqib Khan #include <elog-errors.hpp> 8dcbfa04aSSaqib Khan #include <xyz/openbmc_project/Software/Version/error.hpp> 9ec1b41c4SGunnar Mills #include "config.h" 102ce7da29SGunnar Mills #include "item_updater.hpp" 112ce7da29SGunnar Mills #include "xyz/openbmc_project/Software/Version/server.hpp" 1235e83f3eSSaqib Khan #include <experimental/filesystem> 13705f1bfcSSaqib Khan #include "version.hpp" 145d532675SSaqib Khan #include "serialize.hpp" 15ec1b41c4SGunnar Mills 16ec1b41c4SGunnar Mills namespace phosphor 17ec1b41c4SGunnar Mills { 18ec1b41c4SGunnar Mills namespace software 19ec1b41c4SGunnar Mills { 20ec1b41c4SGunnar Mills namespace updater 21ec1b41c4SGunnar Mills { 22ec1b41c4SGunnar Mills 232ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class 242ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server; 250129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server; 262ce7da29SGunnar Mills 272ce7da29SGunnar Mills using namespace phosphor::logging; 28dcbfa04aSSaqib Khan using namespace sdbusplus::xyz::openbmc_project::Software::Version::Error; 2935e83f3eSSaqib Khan namespace fs = std::experimental::filesystem; 3035e83f3eSSaqib Khan 31*2285fe0fSAdriana Kobylak const std::vector<std::string> bmcImages = {"image-kernel", "image-rofs", 32*2285fe0fSAdriana Kobylak "image-rwfs", "image-u-boot"}; 332ce7da29SGunnar Mills 34e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg) 35ec1b41c4SGunnar Mills { 3684a0e693SSaqib Khan 3784a0e693SSaqib Khan using SVersion = server::Version; 3884a0e693SSaqib Khan using VersionPurpose = SVersion::VersionPurpose; 399a782243SGunnar Mills using VersionClass = phosphor::software::manager::Version; 4084a0e693SSaqib Khan namespace mesg = sdbusplus::message; 4184a0e693SSaqib Khan namespace variant_ns = mesg::variant_ns; 4284a0e693SSaqib Khan 4384a0e693SSaqib Khan mesg::object_path objPath; 4484a0e693SSaqib Khan auto purpose = VersionPurpose::Unknown; 45705f1bfcSSaqib Khan std::string version; 46*2285fe0fSAdriana Kobylak std::map<std::string, std::map<std::string, mesg::variant<std::string>>> 47*2285fe0fSAdriana Kobylak interfaces; 48e75d10f5SPatrick Williams msg.read(objPath, interfaces); 492ce7da29SGunnar Mills std::string path(std::move(objPath)); 5019177d3eSSaqib Khan std::string filePath; 512ce7da29SGunnar Mills 522ce7da29SGunnar Mills for (const auto& intf : interfaces) 532ce7da29SGunnar Mills { 54705f1bfcSSaqib Khan if (intf.first == VERSION_IFACE) 552ce7da29SGunnar Mills { 562ce7da29SGunnar Mills for (const auto& property : intf.second) 572ce7da29SGunnar Mills { 58705f1bfcSSaqib Khan if (property.first == "Purpose") 592ce7da29SGunnar Mills { 6084a0e693SSaqib Khan auto value = SVersion::convertVersionPurposeFromString( 6184a0e693SSaqib Khan variant_ns::get<std::string>(property.second)); 6284a0e693SSaqib Khan if (value == VersionPurpose::BMC || 6384a0e693SSaqib Khan value == VersionPurpose::System) 6484a0e693SSaqib Khan { 6584a0e693SSaqib Khan purpose = value; 6684a0e693SSaqib Khan } 67705f1bfcSSaqib Khan } 68705f1bfcSSaqib Khan else if (property.first == "Version") 69705f1bfcSSaqib Khan { 7084a0e693SSaqib Khan version = variant_ns::get<std::string>(property.second); 71705f1bfcSSaqib Khan } 72705f1bfcSSaqib Khan } 73705f1bfcSSaqib Khan } 7419177d3eSSaqib Khan else if (intf.first == FILEPATH_IFACE) 7519177d3eSSaqib Khan { 7619177d3eSSaqib Khan for (const auto& property : intf.second) 7719177d3eSSaqib Khan { 7819177d3eSSaqib Khan if (property.first == "Path") 7919177d3eSSaqib Khan { 8084a0e693SSaqib Khan filePath = variant_ns::get<std::string>(property.second); 8119177d3eSSaqib Khan } 8219177d3eSSaqib Khan } 8319177d3eSSaqib Khan } 84705f1bfcSSaqib Khan } 85*2285fe0fSAdriana Kobylak if (version.empty() || filePath.empty() || 8684a0e693SSaqib Khan purpose == VersionPurpose::Unknown) 872ce7da29SGunnar Mills { 88e75d10f5SPatrick Williams return; 892ce7da29SGunnar Mills } 902ce7da29SGunnar Mills 912ce7da29SGunnar Mills // Version id is the last item in the path 922ce7da29SGunnar Mills auto pos = path.rfind("/"); 932ce7da29SGunnar Mills if (pos == std::string::npos) 942ce7da29SGunnar Mills { 952ce7da29SGunnar Mills log<level::ERR>("No version id found in object path", 96596466b8SAdriana Kobylak entry("OBJPATH=%s", path.c_str())); 97e75d10f5SPatrick Williams return; 982ce7da29SGunnar Mills } 992ce7da29SGunnar Mills 1002ce7da29SGunnar Mills auto versionId = path.substr(pos + 1); 1012ce7da29SGunnar Mills 102e75d10f5SPatrick Williams if (activations.find(versionId) == activations.end()) 1032ce7da29SGunnar Mills { 10435e83f3eSSaqib Khan // Determine the Activation state by processing the given image dir. 10535e83f3eSSaqib Khan auto activationState = server::Activation::Activations::Invalid; 1069a782243SGunnar Mills ItemUpdater::ActivationStatus result = 1079a782243SGunnar Mills ItemUpdater::validateSquashFSImage(filePath); 10843b25cdeSGunnar Mills AssociationList associations = {}; 10943b25cdeSGunnar Mills 11035e83f3eSSaqib Khan if (result == ItemUpdater::ActivationStatus::ready) 11135e83f3eSSaqib Khan { 11235e83f3eSSaqib Khan activationState = server::Activation::Activations::Ready; 113b60add1eSGunnar Mills // Create an association to the BMC inventory item 114*2285fe0fSAdriana Kobylak associations.emplace_back( 115*2285fe0fSAdriana Kobylak std::make_tuple(ACTIVATION_FWD_ASSOCIATION, 116*2285fe0fSAdriana Kobylak ACTIVATION_REV_ASSOCIATION, bmcInventoryPath)); 11743b25cdeSGunnar Mills } 118b60add1eSGunnar Mills 119ee13e831SSaqib Khan activations.insert(std::make_pair( 120ee13e831SSaqib Khan versionId, 121*2285fe0fSAdriana Kobylak std::make_unique<Activation>(bus, path, *this, versionId, 122*2285fe0fSAdriana Kobylak activationState, associations))); 1234254beceSMichael Tritz 124ee13e831SSaqib Khan auto versionPtr = std::make_unique<VersionClass>( 125*2285fe0fSAdriana Kobylak bus, path, version, purpose, filePath, 126*2285fe0fSAdriana Kobylak std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); 127ee13e831SSaqib Khan versionPtr->deleteObject = 128*2285fe0fSAdriana Kobylak std::make_unique<phosphor::software::manager::Delete>(bus, path, 129*2285fe0fSAdriana Kobylak *versionPtr); 130ee13e831SSaqib Khan versions.insert(std::make_pair(versionId, std::move(versionPtr))); 1312ce7da29SGunnar Mills } 132e75d10f5SPatrick Williams return; 133ec1b41c4SGunnar Mills } 134ec1b41c4SGunnar Mills 135ba239881SSaqib Khan void ItemUpdater::processBMCImage() 136ba239881SSaqib Khan { 13788e8a325SGunnar Mills using VersionClass = phosphor::software::manager::Version; 13888e8a325SGunnar Mills // Read os-release from /etc/ to get the functional BMC version 13988e8a325SGunnar Mills auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE); 14088e8a325SGunnar Mills 1411eef62deSSaqib Khan // Read os-release from folders under /media/ to get 1421eef62deSSaqib Khan // BMC Software Versions. 1431eef62deSSaqib Khan for (const auto& iter : fs::directory_iterator(MEDIA_DIR)) 1441eef62deSSaqib Khan { 1451eef62deSSaqib Khan auto activationState = server::Activation::Activations::Active; 1466fab70daSSaqib Khan static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX); 1471eef62deSSaqib Khan 1481eef62deSSaqib Khan // Check if the BMC_RO_PREFIXis the prefix of the iter.path 149*2285fe0fSAdriana Kobylak if (0 == 150*2285fe0fSAdriana Kobylak iter.path().native().compare(0, BMC_RO_PREFIX_LEN, BMC_ROFS_PREFIX)) 1511eef62deSSaqib Khan { 152021c365bSSaqib Khan // The versionId is extracted from the path 153021c365bSSaqib Khan // for example /media/ro-2a1022fe. 154021c365bSSaqib Khan auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN); 1551eef62deSSaqib Khan auto osRelease = iter.path() / OS_RELEASE_FILE; 1561eef62deSSaqib Khan if (!fs::is_regular_file(osRelease)) 1571eef62deSSaqib Khan { 158*2285fe0fSAdriana Kobylak log<level::ERR>( 159*2285fe0fSAdriana Kobylak "Failed to read osRelease", 160596466b8SAdriana Kobylak entry("FILENAME=%s", osRelease.string().c_str())); 161021c365bSSaqib Khan ItemUpdater::erase(id); 162021c365bSSaqib Khan continue; 1631eef62deSSaqib Khan } 16488e8a325SGunnar Mills auto version = VersionClass::getBMCVersion(osRelease); 1651eef62deSSaqib Khan if (version.empty()) 1661eef62deSSaqib Khan { 167*2285fe0fSAdriana Kobylak log<level::ERR>( 168*2285fe0fSAdriana Kobylak "Failed to read version from osRelease", 169596466b8SAdriana Kobylak entry("FILENAME=%s", osRelease.string().c_str())); 1701eef62deSSaqib Khan activationState = server::Activation::Activations::Invalid; 1711eef62deSSaqib Khan } 172021c365bSSaqib Khan 1731eef62deSSaqib Khan auto purpose = server::Version::VersionPurpose::BMC; 1741eef62deSSaqib Khan auto path = fs::path(SOFTWARE_OBJPATH) / id; 1751eef62deSSaqib Khan 17688e8a325SGunnar Mills // Create functional association if this is the functional version 17788e8a325SGunnar Mills if (version.compare(functionalVersion) == 0) 17888e8a325SGunnar Mills { 17988e8a325SGunnar Mills createFunctionalAssociation(path); 18088e8a325SGunnar Mills } 18188e8a325SGunnar Mills 18243b25cdeSGunnar Mills AssociationList associations = {}; 18343b25cdeSGunnar Mills 18443b25cdeSGunnar Mills if (activationState == server::Activation::Activations::Active) 18543b25cdeSGunnar Mills { 18643b25cdeSGunnar Mills // Create an association to the BMC inventory item 18743b25cdeSGunnar Mills associations.emplace_back(std::make_tuple( 188*2285fe0fSAdriana Kobylak ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION, 18943b25cdeSGunnar Mills bmcInventoryPath)); 19043b25cdeSGunnar Mills 19143b25cdeSGunnar Mills // Create an active association since this image is active 19243b25cdeSGunnar Mills createActiveAssociation(path); 19343b25cdeSGunnar Mills } 19443b25cdeSGunnar Mills 195ee590c74SAdriana Kobylak // Create Version instance for this version. 196ee590c74SAdriana Kobylak auto versionPtr = std::make_unique<VersionClass>( 197*2285fe0fSAdriana Kobylak bus, path, version, purpose, "", 198*2285fe0fSAdriana Kobylak std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); 199ee590c74SAdriana Kobylak auto isVersionFunctional = versionPtr->isFunctional(); 200ee13e831SSaqib Khan if (!isVersionFunctional) 201ee13e831SSaqib Khan { 202ee13e831SSaqib Khan versionPtr->deleteObject = 203ee13e831SSaqib Khan std::make_unique<phosphor::software::manager::Delete>( 204ee13e831SSaqib Khan bus, path, *versionPtr); 205ee13e831SSaqib Khan } 206*2285fe0fSAdriana Kobylak versions.insert(std::make_pair(id, std::move(versionPtr))); 207ee590c74SAdriana Kobylak 2081eef62deSSaqib Khan // Create Activation instance for this version. 209ee13e831SSaqib Khan activations.insert(std::make_pair( 210*2285fe0fSAdriana Kobylak id, std::make_unique<Activation>( 211*2285fe0fSAdriana Kobylak bus, path, *this, id, activationState, associations))); 2121eef62deSSaqib Khan 2131eef62deSSaqib Khan // If Active, create RedundancyPriority instance for this version. 2141eef62deSSaqib Khan if (activationState == server::Activation::Activations::Active) 2151eef62deSSaqib Khan { 2161eef62deSSaqib Khan uint8_t priority = std::numeric_limits<uint8_t>::max(); 2171eef62deSSaqib Khan if (!restoreFromFile(id, priority)) 2181eef62deSSaqib Khan { 219ee590c74SAdriana Kobylak if (isVersionFunctional) 220ee590c74SAdriana Kobylak { 221ee590c74SAdriana Kobylak priority = 0; 222ee590c74SAdriana Kobylak } 223ee590c74SAdriana Kobylak else 224ee590c74SAdriana Kobylak { 2251eef62deSSaqib Khan log<level::ERR>("Unable to restore priority from file.", 226596466b8SAdriana Kobylak entry("VERSIONID=%s", id.c_str())); 2271eef62deSSaqib Khan } 228ee590c74SAdriana Kobylak } 2291eef62deSSaqib Khan activations.find(id)->second->redundancyPriority = 2301eef62deSSaqib Khan std::make_unique<RedundancyPriority>( 231*2285fe0fSAdriana Kobylak bus, path, *(activations.find(id)->second), priority, 232b77551cdSAdriana Kobylak false); 2331eef62deSSaqib Khan } 2341eef62deSSaqib Khan } 2351eef62deSSaqib Khan } 236dcbfa04aSSaqib Khan 237dcbfa04aSSaqib Khan // If there is no ubi volume for bmc version then read the /etc/os-release 238dcbfa04aSSaqib Khan // and create rofs-<versionId> under /media 239dcbfa04aSSaqib Khan if (activations.size() == 0) 240dcbfa04aSSaqib Khan { 241d16bcbd5SGunnar Mills auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE); 242dcbfa04aSSaqib Khan auto id = phosphor::software::manager::Version::getId(version); 243dcbfa04aSSaqib Khan auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/"; 244dcbfa04aSSaqib Khan try 245dcbfa04aSSaqib Khan { 246dcbfa04aSSaqib Khan if (!fs::is_directory(versionFileDir)) 247dcbfa04aSSaqib Khan { 248dcbfa04aSSaqib Khan fs::create_directories(versionFileDir); 249dcbfa04aSSaqib Khan } 250dcbfa04aSSaqib Khan auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE; 251dcbfa04aSSaqib Khan fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath); 252dcbfa04aSSaqib Khan ItemUpdater::processBMCImage(); 253dcbfa04aSSaqib Khan } 254dcbfa04aSSaqib Khan catch (const std::exception& e) 255dcbfa04aSSaqib Khan { 256dcbfa04aSSaqib Khan log<level::ERR>(e.what()); 257dcbfa04aSSaqib Khan } 258dcbfa04aSSaqib Khan } 259ba239881SSaqib Khan return; 260ba239881SSaqib Khan } 261ba239881SSaqib Khan 2623526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId) 2633526ef73SLeonel Gonzalez { 2646d873715SEddie James // Find entry in versions map 2656d873715SEddie James auto it = versions.find(entryId); 2666d873715SEddie James if (it != versions.end()) 2676d873715SEddie James { 2686d873715SEddie James if (it->second->isFunctional()) 2696d873715SEddie James { 270*2285fe0fSAdriana Kobylak log<level::ERR>("Error: Version is currently running on the BMC. " 271*2285fe0fSAdriana Kobylak "Unable to remove.", 272*2285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 2736d873715SEddie James return; 2746d873715SEddie James } 2756d873715SEddie James 2766d873715SEddie James // Delete ReadOnly partitions if it's not active 2773526ef73SLeonel Gonzalez removeReadOnlyPartition(entryId); 2781eef62deSSaqib Khan removeFile(entryId); 279ee13e831SSaqib Khan 280ee13e831SSaqib Khan // Removing entry in versions map 281ee13e831SSaqib Khan this->versions.erase(entryId); 2826d873715SEddie James } 2836d873715SEddie James else 2846d873715SEddie James { 2856d873715SEddie James // Delete ReadOnly partitions even if we can't find the version 2866d873715SEddie James removeReadOnlyPartition(entryId); 2876d873715SEddie James removeFile(entryId); 2886d873715SEddie James 289*2285fe0fSAdriana Kobylak log<level::ERR>("Error: Failed to find version in item updater " 290*2285fe0fSAdriana Kobylak "versions map. Unable to remove.", 291*2285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 2926d873715SEddie James } 2931eef62deSSaqib Khan 2941eef62deSSaqib Khan // Remove the priority environment variable. 2951eef62deSSaqib Khan auto serviceFile = "obmc-flash-bmc-setenv@" + entryId + ".service"; 296*2285fe0fSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 297*2285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 2981eef62deSSaqib Khan method.append(serviceFile, "replace"); 2991eef62deSSaqib Khan bus.call_noreply(method); 3003526ef73SLeonel Gonzalez 3013526ef73SLeonel Gonzalez // Removing entry in activations map 3023526ef73SLeonel Gonzalez auto ita = activations.find(entryId); 3033526ef73SLeonel Gonzalez if (ita == activations.end()) 3043526ef73SLeonel Gonzalez { 305*2285fe0fSAdriana Kobylak log<level::ERR>("Error: Failed to find version in item updater " 306*2285fe0fSAdriana Kobylak "activations map. Unable to remove.", 307*2285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 3083526ef73SLeonel Gonzalez } 309ee13e831SSaqib Khan else 310ee13e831SSaqib Khan { 3113526ef73SLeonel Gonzalez this->activations.erase(entryId); 312ee13e831SSaqib Khan } 31349446ae9SSaqib Khan ItemUpdater::resetUbootEnvVars(); 314ee13e831SSaqib Khan return; 3153526ef73SLeonel Gonzalez } 3163526ef73SLeonel Gonzalez 317bc1bf3afSMichael Tritz void ItemUpdater::deleteAll() 318bc1bf3afSMichael Tritz { 31983cd21fbSAdriana Kobylak std::vector<std::string> deletableVersions; 32083cd21fbSAdriana Kobylak 321bc1bf3afSMichael Tritz for (const auto& versionIt : versions) 322bc1bf3afSMichael Tritz { 323bc1bf3afSMichael Tritz if (!versionIt.second->isFunctional()) 324bc1bf3afSMichael Tritz { 32583cd21fbSAdriana Kobylak deletableVersions.push_back(versionIt.first); 326bc1bf3afSMichael Tritz } 327bc1bf3afSMichael Tritz } 328bc1bf3afSMichael Tritz 32983cd21fbSAdriana Kobylak for (const auto& deletableIt : deletableVersions) 33083cd21fbSAdriana Kobylak { 33183cd21fbSAdriana Kobylak ItemUpdater::erase(deletableIt); 33283cd21fbSAdriana Kobylak } 33383cd21fbSAdriana Kobylak 334bc1bf3afSMichael Tritz // Remove any volumes that do not match current versions. 335*2285fe0fSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 336*2285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 337bc1bf3afSMichael Tritz method.append("obmc-flash-bmc-cleanup.service", "replace"); 338bc1bf3afSMichael Tritz bus.call_noreply(method); 339bc1bf3afSMichael Tritz } 340bc1bf3afSMichael Tritz 341*2285fe0fSAdriana Kobylak ItemUpdater::ActivationStatus 342*2285fe0fSAdriana Kobylak ItemUpdater::validateSquashFSImage(const std::string& filePath) 34335e83f3eSSaqib Khan { 344b1cfdf99SMichael Tritz bool invalid = false; 34535e83f3eSSaqib Khan 346b1cfdf99SMichael Tritz for (auto& bmcImage : bmcImages) 347b1cfdf99SMichael Tritz { 34819177d3eSSaqib Khan fs::path file(filePath); 34935e83f3eSSaqib Khan file /= bmcImage; 35035e83f3eSSaqib Khan std::ifstream efile(file.c_str()); 351b1cfdf99SMichael Tritz if (efile.good() != 1) 35235e83f3eSSaqib Khan { 353b1cfdf99SMichael Tritz log<level::ERR>("Failed to find the BMC image.", 354b1cfdf99SMichael Tritz entry("IMAGE=%s", bmcImage.c_str())); 355b1cfdf99SMichael Tritz invalid = true; 35635e83f3eSSaqib Khan } 357b1cfdf99SMichael Tritz } 358b1cfdf99SMichael Tritz 359b1cfdf99SMichael Tritz if (invalid) 36035e83f3eSSaqib Khan { 36135e83f3eSSaqib Khan return ItemUpdater::ActivationStatus::invalid; 36235e83f3eSSaqib Khan } 363b1cfdf99SMichael Tritz 364b1cfdf99SMichael Tritz return ItemUpdater::ActivationStatus::ready; 36535e83f3eSSaqib Khan } 36635e83f3eSSaqib Khan 367b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId) 3684c1aec09SSaqib Khan { 369b77551cdSAdriana Kobylak std::map<std::string, uint8_t> priorityMap; 370b77551cdSAdriana Kobylak 371b77551cdSAdriana Kobylak // Insert the requested version and priority, it may not exist yet. 372b77551cdSAdriana Kobylak priorityMap.insert(std::make_pair(versionId, value)); 373b77551cdSAdriana Kobylak 3744c1aec09SSaqib Khan for (const auto& intf : activations) 3754c1aec09SSaqib Khan { 3764c1aec09SSaqib Khan if (intf.second->redundancyPriority) 3774c1aec09SSaqib Khan { 378b77551cdSAdriana Kobylak priorityMap.insert(std::make_pair( 379*2285fe0fSAdriana Kobylak intf.first, intf.second->redundancyPriority.get()->priority())); 380b77551cdSAdriana Kobylak } 381b77551cdSAdriana Kobylak } 382b77551cdSAdriana Kobylak 383b77551cdSAdriana Kobylak // Lambda function to compare 2 priority values, use <= to allow duplicates 384*2285fe0fSAdriana Kobylak typedef std::function<bool(std::pair<std::string, uint8_t>, 385*2285fe0fSAdriana Kobylak std::pair<std::string, uint8_t>)> 386*2285fe0fSAdriana Kobylak cmpPriority; 387*2285fe0fSAdriana Kobylak cmpPriority cmpPriorityFunc = 388*2285fe0fSAdriana Kobylak [](std::pair<std::string, uint8_t> priority1, 389*2285fe0fSAdriana Kobylak std::pair<std::string, uint8_t> priority2) { 390b77551cdSAdriana Kobylak return priority1.second <= priority2.second; 391b77551cdSAdriana Kobylak }; 392b77551cdSAdriana Kobylak 393b77551cdSAdriana Kobylak // Sort versions by ascending priority 394b77551cdSAdriana Kobylak std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet( 395b77551cdSAdriana Kobylak priorityMap.begin(), priorityMap.end(), cmpPriorityFunc); 396b77551cdSAdriana Kobylak 397b77551cdSAdriana Kobylak auto freePriorityValue = value; 398b77551cdSAdriana Kobylak for (auto& element : prioritySet) 399b77551cdSAdriana Kobylak { 400b77551cdSAdriana Kobylak if (element.first == versionId) 401b77551cdSAdriana Kobylak { 402b77551cdSAdriana Kobylak continue; 403b77551cdSAdriana Kobylak } 404b77551cdSAdriana Kobylak if (element.second == freePriorityValue) 405b77551cdSAdriana Kobylak { 406b77551cdSAdriana Kobylak ++freePriorityValue; 407b77551cdSAdriana Kobylak auto it = activations.find(element.first); 408b77551cdSAdriana Kobylak it->second->redundancyPriority.get()->sdbusPriority( 409b77551cdSAdriana Kobylak freePriorityValue); 4104c1aec09SSaqib Khan } 4114c1aec09SSaqib Khan } 412b77551cdSAdriana Kobylak 413b77551cdSAdriana Kobylak auto lowestVersion = prioritySet.begin()->first; 414b77551cdSAdriana Kobylak if (value == prioritySet.begin()->second) 415b77551cdSAdriana Kobylak { 416b77551cdSAdriana Kobylak lowestVersion = versionId; 4174c1aec09SSaqib Khan } 418b77551cdSAdriana Kobylak updateUbootEnvVars(lowestVersion); 4194c1aec09SSaqib Khan } 4204c1aec09SSaqib Khan 42137a59043SMichael Tritz void ItemUpdater::reset() 42237a59043SMichael Tritz { 42337a59043SMichael Tritz // Mark the read-write partition for recreation upon reboot. 424*2285fe0fSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 425*2285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 4260129d926SMichael Tritz method.append("obmc-flash-bmc-setenv@rwreset\\x3dtrue.service", "replace"); 42737a59043SMichael Tritz bus.call_noreply(method); 42837a59043SMichael Tritz 42937a59043SMichael Tritz log<level::INFO>("BMC factory reset will take effect upon reboot."); 43037a59043SMichael Tritz 43137a59043SMichael Tritz return; 43237a59043SMichael Tritz } 43337a59043SMichael Tritz 4343526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId) 4353526ef73SLeonel Gonzalez { 436*2285fe0fSAdriana Kobylak auto serviceFile = "obmc-flash-bmc-ubiro-remove@" + versionId + ".service"; 4373526ef73SLeonel Gonzalez 4383526ef73SLeonel Gonzalez // Remove the read-only partitions. 439*2285fe0fSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 440*2285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 4413526ef73SLeonel Gonzalez method.append(serviceFile, "replace"); 4423526ef73SLeonel Gonzalez bus.call_noreply(method); 4433526ef73SLeonel Gonzalez } 4443526ef73SLeonel Gonzalez 4450129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value) 4460129d926SMichael Tritz { 4470129d926SMichael Tritz // enabling field mode is intended to be one way: false -> true 4480129d926SMichael Tritz if (value && !control::FieldMode::fieldModeEnabled()) 4490129d926SMichael Tritz { 4500129d926SMichael Tritz control::FieldMode::fieldModeEnabled(value); 4510129d926SMichael Tritz 452*2285fe0fSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 453*2285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 4540129d926SMichael Tritz method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service", 4550129d926SMichael Tritz "replace"); 4560129d926SMichael Tritz bus.call_noreply(method); 4570129d926SMichael Tritz 458*2285fe0fSAdriana Kobylak method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 459*2285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StopUnit"); 4600129d926SMichael Tritz method.append("usr-local.mount", "replace"); 4610129d926SMichael Tritz bus.call_noreply(method); 4620129d926SMichael Tritz 4630129d926SMichael Tritz std::vector<std::string> usrLocal = {"usr-local.mount"}; 4640129d926SMichael Tritz 465*2285fe0fSAdriana Kobylak method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 466*2285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "MaskUnitFiles"); 4670129d926SMichael Tritz method.append(usrLocal, false, true); 4680129d926SMichael Tritz bus.call_noreply(method); 4690129d926SMichael Tritz } 4700129d926SMichael Tritz 4710129d926SMichael Tritz return control::FieldMode::fieldModeEnabled(); 4720129d926SMichael Tritz } 4730129d926SMichael Tritz 4740129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus() 4750129d926SMichael Tritz { 476ff0b421dSMichael Tritz std::ifstream input("/dev/mtd/u-boot-env"); 4770129d926SMichael Tritz std::string envVar; 4780129d926SMichael Tritz std::getline(input, envVar); 4790129d926SMichael Tritz 4800129d926SMichael Tritz if (envVar.find("fieldmode=true") != std::string::npos) 4810129d926SMichael Tritz { 4820129d926SMichael Tritz ItemUpdater::fieldModeEnabled(true); 4830129d926SMichael Tritz } 4840129d926SMichael Tritz } 4850129d926SMichael Tritz 486b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath() 487b60add1eSGunnar Mills { 488b60add1eSGunnar Mills auto depth = 0; 489*2285fe0fSAdriana Kobylak auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 490*2285fe0fSAdriana Kobylak MAPPER_INTERFACE, "GetSubTreePaths"); 491b60add1eSGunnar Mills 4921254c628SAdriana Kobylak mapperCall.append(INVENTORY_PATH); 493b60add1eSGunnar Mills mapperCall.append(depth); 4941254c628SAdriana Kobylak std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE}; 495b60add1eSGunnar Mills mapperCall.append(filter); 496b60add1eSGunnar Mills 497b60add1eSGunnar Mills auto response = bus.call(mapperCall); 498b60add1eSGunnar Mills if (response.is_method_error()) 499b60add1eSGunnar Mills { 500b60add1eSGunnar Mills log<level::ERR>("Error in mapper GetSubTreePath"); 501b60add1eSGunnar Mills return; 502b60add1eSGunnar Mills } 503b60add1eSGunnar Mills 504b60add1eSGunnar Mills using ObjectPaths = std::vector<std::string>; 505b60add1eSGunnar Mills ObjectPaths result; 506b60add1eSGunnar Mills response.read(result); 507b60add1eSGunnar Mills 5081254c628SAdriana Kobylak if (!result.empty()) 509b60add1eSGunnar Mills { 5101254c628SAdriana Kobylak bmcInventoryPath = result.front(); 511b60add1eSGunnar Mills } 512b60add1eSGunnar Mills 513b60add1eSGunnar Mills return; 514b60add1eSGunnar Mills } 515b60add1eSGunnar Mills 516f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path) 517ded875dcSGunnar Mills { 518*2285fe0fSAdriana Kobylak assocs.emplace_back( 519*2285fe0fSAdriana Kobylak std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path)); 520ded875dcSGunnar Mills associations(assocs); 521ded875dcSGunnar Mills } 522ded875dcSGunnar Mills 52388e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path) 52488e8a325SGunnar Mills { 52588e8a325SGunnar Mills assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION, 526*2285fe0fSAdriana Kobylak FUNCTIONAL_REV_ASSOCIATION, path)); 52788e8a325SGunnar Mills associations(assocs); 52888e8a325SGunnar Mills } 52988e8a325SGunnar Mills 530f10b2326SGunnar Mills void ItemUpdater::removeActiveAssociation(const std::string& path) 531ded875dcSGunnar Mills { 532ded875dcSGunnar Mills for (auto iter = assocs.begin(); iter != assocs.end();) 533ded875dcSGunnar Mills { 53488e8a325SGunnar Mills // Since there could be multiple associations to the same path, 53588e8a325SGunnar Mills // only remove ones that have an active forward association. 53688e8a325SGunnar Mills if ((std::get<0>(*iter)).compare(ACTIVE_FWD_ASSOCIATION) == 0 && 53788e8a325SGunnar Mills (std::get<2>(*iter)).compare(path) == 0) 538ded875dcSGunnar Mills { 539ded875dcSGunnar Mills iter = assocs.erase(iter); 540ded875dcSGunnar Mills associations(assocs); 541ded875dcSGunnar Mills } 542ded875dcSGunnar Mills else 543ded875dcSGunnar Mills { 544ded875dcSGunnar Mills ++iter; 545ded875dcSGunnar Mills } 546ded875dcSGunnar Mills } 547ded875dcSGunnar Mills } 548ded875dcSGunnar Mills 549b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value) 550b9da6634SSaqib Khan { 551b9da6634SSaqib Khan for (const auto& intf : activations) 552b9da6634SSaqib Khan { 553b9da6634SSaqib Khan if (intf.second->redundancyPriority) 554b9da6634SSaqib Khan { 555b9da6634SSaqib Khan if (intf.second->redundancyPriority.get()->priority() < value) 556b9da6634SSaqib Khan { 557b9da6634SSaqib Khan return false; 558b9da6634SSaqib Khan } 559b9da6634SSaqib Khan } 560b9da6634SSaqib Khan } 561b9da6634SSaqib Khan return true; 562b9da6634SSaqib Khan } 563b9da6634SSaqib Khan 564b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId) 565b77551cdSAdriana Kobylak { 566*2285fe0fSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 567*2285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 568*2285fe0fSAdriana Kobylak auto updateEnvVarsFile = 569*2285fe0fSAdriana Kobylak "obmc-flash-bmc-updateubootvars@" + versionId + ".service"; 570b77551cdSAdriana Kobylak method.append(updateEnvVarsFile, "replace"); 571b77551cdSAdriana Kobylak auto result = bus.call(method); 572b77551cdSAdriana Kobylak 573b77551cdSAdriana Kobylak // Check that the bus call didn't result in an error 574b77551cdSAdriana Kobylak if (result.is_method_error()) 575b77551cdSAdriana Kobylak { 576b77551cdSAdriana Kobylak log<level::ERR>("Failed to update u-boot env variables", 577596466b8SAdriana Kobylak entry("VERSIONID=%s", versionId.c_str())); 578b77551cdSAdriana Kobylak } 579b77551cdSAdriana Kobylak } 580b77551cdSAdriana Kobylak 58149446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars() 58249446ae9SSaqib Khan { 58349446ae9SSaqib Khan decltype(activations.begin()->second->redundancyPriority.get()->priority()) 58449446ae9SSaqib Khan lowestPriority = std::numeric_limits<uint8_t>::max(); 58549446ae9SSaqib Khan decltype(activations.begin()->second->versionId) lowestPriorityVersion; 58649446ae9SSaqib Khan for (const auto& intf : activations) 58749446ae9SSaqib Khan { 58849446ae9SSaqib Khan if (!intf.second->redundancyPriority.get()) 58949446ae9SSaqib Khan { 59049446ae9SSaqib Khan // Skip this version if the redundancyPriority is not initialized. 59149446ae9SSaqib Khan continue; 59249446ae9SSaqib Khan } 59349446ae9SSaqib Khan 594*2285fe0fSAdriana Kobylak if (intf.second->redundancyPriority.get()->priority() <= lowestPriority) 59549446ae9SSaqib Khan { 59649446ae9SSaqib Khan lowestPriority = intf.second->redundancyPriority.get()->priority(); 59749446ae9SSaqib Khan lowestPriorityVersion = intf.second->versionId; 59849446ae9SSaqib Khan } 59949446ae9SSaqib Khan } 60049446ae9SSaqib Khan 601f0382c35SSaqib Khan // Update the U-boot environment variable to point to the lowest priority 602b77551cdSAdriana Kobylak updateUbootEnvVars(lowestPriorityVersion); 60349446ae9SSaqib Khan } 60449446ae9SSaqib Khan 605204e1e74SAdriana Kobylak void ItemUpdater::freeSpace() 606204e1e74SAdriana Kobylak { 607204e1e74SAdriana Kobylak // Versions with the highest priority in front 608204e1e74SAdriana Kobylak std::priority_queue<std::pair<int, std::string>, 609204e1e74SAdriana Kobylak std::vector<std::pair<int, std::string>>, 610*2285fe0fSAdriana Kobylak std::less<std::pair<int, std::string>>> 611*2285fe0fSAdriana Kobylak versionsPQ; 612204e1e74SAdriana Kobylak 613204e1e74SAdriana Kobylak std::size_t count = 0; 614204e1e74SAdriana Kobylak for (const auto& iter : activations) 615204e1e74SAdriana Kobylak { 616204e1e74SAdriana Kobylak if ((iter.second.get()->activation() == 617204e1e74SAdriana Kobylak server::Activation::Activations::Active) || 618204e1e74SAdriana Kobylak (iter.second.get()->activation() == 619204e1e74SAdriana Kobylak server::Activation::Activations::Failed)) 620204e1e74SAdriana Kobylak { 621204e1e74SAdriana Kobylak count++; 622204e1e74SAdriana Kobylak // Don't put the functional version on the queue since we can't 623204e1e74SAdriana Kobylak // remove the "running" BMC version. 624204e1e74SAdriana Kobylak if (versions.find(iter.second->versionId)->second->isFunctional()) 625204e1e74SAdriana Kobylak { 626204e1e74SAdriana Kobylak continue; 627204e1e74SAdriana Kobylak } 628204e1e74SAdriana Kobylak versionsPQ.push(std::make_pair( 629204e1e74SAdriana Kobylak iter.second->redundancyPriority.get()->priority(), 630204e1e74SAdriana Kobylak iter.second->versionId)); 631204e1e74SAdriana Kobylak } 632204e1e74SAdriana Kobylak } 633204e1e74SAdriana Kobylak 634204e1e74SAdriana Kobylak // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1, 635204e1e74SAdriana Kobylak // remove the highest priority one(s). 636204e1e74SAdriana Kobylak while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty())) 637204e1e74SAdriana Kobylak { 638204e1e74SAdriana Kobylak erase(versionsPQ.top().second); 639204e1e74SAdriana Kobylak versionsPQ.pop(); 640204e1e74SAdriana Kobylak count--; 641204e1e74SAdriana Kobylak } 642204e1e74SAdriana Kobylak } 643204e1e74SAdriana Kobylak 644ec1b41c4SGunnar Mills } // namespace updater 645ec1b41c4SGunnar Mills } // namespace software 646ec1b41c4SGunnar Mills } // namespace phosphor 647