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" 15*1be8d500SLei YU #include "images.hpp" 16ec1b41c4SGunnar Mills 17ec1b41c4SGunnar Mills namespace phosphor 18ec1b41c4SGunnar Mills { 19ec1b41c4SGunnar Mills namespace software 20ec1b41c4SGunnar Mills { 21ec1b41c4SGunnar Mills namespace updater 22ec1b41c4SGunnar Mills { 23ec1b41c4SGunnar Mills 242ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class 252ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server; 260129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server; 272ce7da29SGunnar Mills 282ce7da29SGunnar Mills using namespace phosphor::logging; 29dcbfa04aSSaqib Khan using namespace sdbusplus::xyz::openbmc_project::Software::Version::Error; 302ab9b109SJayanth Othayoth using namespace phosphor::software::image; 3135e83f3eSSaqib Khan namespace fs = std::experimental::filesystem; 3235e83f3eSSaqib Khan 33e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg) 34ec1b41c4SGunnar Mills { 3584a0e693SSaqib Khan 3684a0e693SSaqib Khan using SVersion = server::Version; 3784a0e693SSaqib Khan using VersionPurpose = SVersion::VersionPurpose; 389a782243SGunnar Mills using VersionClass = phosphor::software::manager::Version; 3984a0e693SSaqib Khan namespace mesg = sdbusplus::message; 4084a0e693SSaqib Khan namespace variant_ns = mesg::variant_ns; 4184a0e693SSaqib Khan 4284a0e693SSaqib Khan mesg::object_path objPath; 4384a0e693SSaqib Khan auto purpose = VersionPurpose::Unknown; 44705f1bfcSSaqib Khan std::string version; 452285fe0fSAdriana Kobylak std::map<std::string, std::map<std::string, mesg::variant<std::string>>> 462285fe0fSAdriana Kobylak interfaces; 47e75d10f5SPatrick Williams msg.read(objPath, interfaces); 482ce7da29SGunnar Mills std::string path(std::move(objPath)); 4919177d3eSSaqib Khan std::string filePath; 502ce7da29SGunnar Mills 512ce7da29SGunnar Mills for (const auto& intf : interfaces) 522ce7da29SGunnar Mills { 53705f1bfcSSaqib Khan if (intf.first == VERSION_IFACE) 542ce7da29SGunnar Mills { 552ce7da29SGunnar Mills for (const auto& property : intf.second) 562ce7da29SGunnar Mills { 57705f1bfcSSaqib Khan if (property.first == "Purpose") 582ce7da29SGunnar Mills { 5984a0e693SSaqib Khan auto value = SVersion::convertVersionPurposeFromString( 6084a0e693SSaqib Khan variant_ns::get<std::string>(property.second)); 6184a0e693SSaqib Khan if (value == VersionPurpose::BMC || 6284a0e693SSaqib Khan value == VersionPurpose::System) 6384a0e693SSaqib Khan { 6484a0e693SSaqib Khan purpose = value; 6584a0e693SSaqib Khan } 66705f1bfcSSaqib Khan } 67705f1bfcSSaqib Khan else if (property.first == "Version") 68705f1bfcSSaqib Khan { 6984a0e693SSaqib Khan version = variant_ns::get<std::string>(property.second); 70705f1bfcSSaqib Khan } 71705f1bfcSSaqib Khan } 72705f1bfcSSaqib Khan } 7319177d3eSSaqib Khan else if (intf.first == FILEPATH_IFACE) 7419177d3eSSaqib Khan { 7519177d3eSSaqib Khan for (const auto& property : intf.second) 7619177d3eSSaqib Khan { 7719177d3eSSaqib Khan if (property.first == "Path") 7819177d3eSSaqib Khan { 7984a0e693SSaqib Khan filePath = variant_ns::get<std::string>(property.second); 8019177d3eSSaqib Khan } 8119177d3eSSaqib Khan } 8219177d3eSSaqib Khan } 83705f1bfcSSaqib Khan } 842285fe0fSAdriana Kobylak if (version.empty() || filePath.empty() || 8584a0e693SSaqib Khan purpose == VersionPurpose::Unknown) 862ce7da29SGunnar Mills { 87e75d10f5SPatrick Williams return; 882ce7da29SGunnar Mills } 892ce7da29SGunnar Mills 902ce7da29SGunnar Mills // Version id is the last item in the path 912ce7da29SGunnar Mills auto pos = path.rfind("/"); 922ce7da29SGunnar Mills if (pos == std::string::npos) 932ce7da29SGunnar Mills { 942ce7da29SGunnar Mills log<level::ERR>("No version id found in object path", 95596466b8SAdriana Kobylak entry("OBJPATH=%s", path.c_str())); 96e75d10f5SPatrick Williams return; 972ce7da29SGunnar Mills } 982ce7da29SGunnar Mills 992ce7da29SGunnar Mills auto versionId = path.substr(pos + 1); 1002ce7da29SGunnar Mills 101e75d10f5SPatrick Williams if (activations.find(versionId) == activations.end()) 1022ce7da29SGunnar Mills { 10335e83f3eSSaqib Khan // Determine the Activation state by processing the given image dir. 10435e83f3eSSaqib Khan auto activationState = server::Activation::Activations::Invalid; 1059a782243SGunnar Mills ItemUpdater::ActivationStatus result = 1069a782243SGunnar Mills ItemUpdater::validateSquashFSImage(filePath); 10743b25cdeSGunnar Mills AssociationList associations = {}; 10843b25cdeSGunnar Mills 10935e83f3eSSaqib Khan if (result == ItemUpdater::ActivationStatus::ready) 11035e83f3eSSaqib Khan { 11135e83f3eSSaqib Khan activationState = server::Activation::Activations::Ready; 112b60add1eSGunnar Mills // Create an association to the BMC inventory item 1132285fe0fSAdriana Kobylak associations.emplace_back( 1142285fe0fSAdriana Kobylak std::make_tuple(ACTIVATION_FWD_ASSOCIATION, 1152285fe0fSAdriana Kobylak ACTIVATION_REV_ASSOCIATION, bmcInventoryPath)); 11643b25cdeSGunnar Mills } 117b60add1eSGunnar Mills 118ee13e831SSaqib Khan activations.insert(std::make_pair( 119ee13e831SSaqib Khan versionId, 1202285fe0fSAdriana Kobylak std::make_unique<Activation>(bus, path, *this, versionId, 1212285fe0fSAdriana Kobylak activationState, associations))); 1224254beceSMichael Tritz 123ee13e831SSaqib Khan auto versionPtr = std::make_unique<VersionClass>( 1242285fe0fSAdriana Kobylak bus, path, version, purpose, filePath, 1252285fe0fSAdriana Kobylak std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); 126ee13e831SSaqib Khan versionPtr->deleteObject = 1272285fe0fSAdriana Kobylak std::make_unique<phosphor::software::manager::Delete>(bus, path, 1282285fe0fSAdriana Kobylak *versionPtr); 129ee13e831SSaqib Khan versions.insert(std::make_pair(versionId, std::move(versionPtr))); 1302ce7da29SGunnar Mills } 131e75d10f5SPatrick Williams return; 132ec1b41c4SGunnar Mills } 133ec1b41c4SGunnar Mills 134ba239881SSaqib Khan void ItemUpdater::processBMCImage() 135ba239881SSaqib Khan { 13688e8a325SGunnar Mills using VersionClass = phosphor::software::manager::Version; 13788e8a325SGunnar Mills // Read os-release from /etc/ to get the functional BMC version 13888e8a325SGunnar Mills auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE); 13988e8a325SGunnar Mills 1401eef62deSSaqib Khan // Read os-release from folders under /media/ to get 1411eef62deSSaqib Khan // BMC Software Versions. 1421eef62deSSaqib Khan for (const auto& iter : fs::directory_iterator(MEDIA_DIR)) 1431eef62deSSaqib Khan { 1441eef62deSSaqib Khan auto activationState = server::Activation::Activations::Active; 1456fab70daSSaqib Khan static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX); 1461eef62deSSaqib Khan 1471eef62deSSaqib Khan // Check if the BMC_RO_PREFIXis the prefix of the iter.path 1482285fe0fSAdriana Kobylak if (0 == 1492285fe0fSAdriana Kobylak iter.path().native().compare(0, BMC_RO_PREFIX_LEN, BMC_ROFS_PREFIX)) 1501eef62deSSaqib Khan { 151021c365bSSaqib Khan // The versionId is extracted from the path 152021c365bSSaqib Khan // for example /media/ro-2a1022fe. 153021c365bSSaqib Khan auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN); 1541eef62deSSaqib Khan auto osRelease = iter.path() / OS_RELEASE_FILE; 1551eef62deSSaqib Khan if (!fs::is_regular_file(osRelease)) 1561eef62deSSaqib Khan { 1572285fe0fSAdriana Kobylak log<level::ERR>( 1582285fe0fSAdriana Kobylak "Failed to read osRelease", 159596466b8SAdriana Kobylak entry("FILENAME=%s", osRelease.string().c_str())); 160021c365bSSaqib Khan ItemUpdater::erase(id); 161021c365bSSaqib Khan continue; 1621eef62deSSaqib Khan } 16388e8a325SGunnar Mills auto version = VersionClass::getBMCVersion(osRelease); 1641eef62deSSaqib Khan if (version.empty()) 1651eef62deSSaqib Khan { 1662285fe0fSAdriana Kobylak log<level::ERR>( 1672285fe0fSAdriana Kobylak "Failed to read version from osRelease", 168596466b8SAdriana Kobylak entry("FILENAME=%s", osRelease.string().c_str())); 1691eef62deSSaqib Khan activationState = server::Activation::Activations::Invalid; 1701eef62deSSaqib Khan } 171021c365bSSaqib Khan 1721eef62deSSaqib Khan auto purpose = server::Version::VersionPurpose::BMC; 1731eef62deSSaqib Khan auto path = fs::path(SOFTWARE_OBJPATH) / id; 1741eef62deSSaqib Khan 17588e8a325SGunnar Mills // Create functional association if this is the functional version 17688e8a325SGunnar Mills if (version.compare(functionalVersion) == 0) 17788e8a325SGunnar Mills { 17888e8a325SGunnar Mills createFunctionalAssociation(path); 17988e8a325SGunnar Mills } 18088e8a325SGunnar Mills 18143b25cdeSGunnar Mills AssociationList associations = {}; 18243b25cdeSGunnar Mills 18343b25cdeSGunnar Mills if (activationState == server::Activation::Activations::Active) 18443b25cdeSGunnar Mills { 18543b25cdeSGunnar Mills // Create an association to the BMC inventory item 18643b25cdeSGunnar Mills associations.emplace_back(std::make_tuple( 1872285fe0fSAdriana Kobylak ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION, 18843b25cdeSGunnar Mills bmcInventoryPath)); 18943b25cdeSGunnar Mills 19043b25cdeSGunnar Mills // Create an active association since this image is active 19143b25cdeSGunnar Mills createActiveAssociation(path); 19243b25cdeSGunnar Mills } 19343b25cdeSGunnar Mills 194ee590c74SAdriana Kobylak // Create Version instance for this version. 195ee590c74SAdriana Kobylak auto versionPtr = std::make_unique<VersionClass>( 1962285fe0fSAdriana Kobylak bus, path, version, purpose, "", 1972285fe0fSAdriana Kobylak std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); 198ee590c74SAdriana Kobylak auto isVersionFunctional = versionPtr->isFunctional(); 199ee13e831SSaqib Khan if (!isVersionFunctional) 200ee13e831SSaqib Khan { 201ee13e831SSaqib Khan versionPtr->deleteObject = 202ee13e831SSaqib Khan std::make_unique<phosphor::software::manager::Delete>( 203ee13e831SSaqib Khan bus, path, *versionPtr); 204ee13e831SSaqib Khan } 2052285fe0fSAdriana Kobylak versions.insert(std::make_pair(id, std::move(versionPtr))); 206ee590c74SAdriana Kobylak 2071eef62deSSaqib Khan // Create Activation instance for this version. 208ee13e831SSaqib Khan activations.insert(std::make_pair( 2092285fe0fSAdriana Kobylak id, std::make_unique<Activation>( 2102285fe0fSAdriana Kobylak bus, path, *this, id, activationState, associations))); 2111eef62deSSaqib Khan 2121eef62deSSaqib Khan // If Active, create RedundancyPriority instance for this version. 2131eef62deSSaqib Khan if (activationState == server::Activation::Activations::Active) 2141eef62deSSaqib Khan { 2151eef62deSSaqib Khan uint8_t priority = std::numeric_limits<uint8_t>::max(); 2161eef62deSSaqib Khan if (!restoreFromFile(id, priority)) 2171eef62deSSaqib Khan { 218ee590c74SAdriana Kobylak if (isVersionFunctional) 219ee590c74SAdriana Kobylak { 220ee590c74SAdriana Kobylak priority = 0; 221ee590c74SAdriana Kobylak } 222ee590c74SAdriana Kobylak else 223ee590c74SAdriana Kobylak { 2241eef62deSSaqib Khan log<level::ERR>("Unable to restore priority from file.", 225596466b8SAdriana Kobylak entry("VERSIONID=%s", id.c_str())); 2261eef62deSSaqib Khan } 227ee590c74SAdriana Kobylak } 2281eef62deSSaqib Khan activations.find(id)->second->redundancyPriority = 2291eef62deSSaqib Khan std::make_unique<RedundancyPriority>( 2302285fe0fSAdriana Kobylak bus, path, *(activations.find(id)->second), priority, 231b77551cdSAdriana Kobylak false); 2321eef62deSSaqib Khan } 2331eef62deSSaqib Khan } 2341eef62deSSaqib Khan } 235dcbfa04aSSaqib Khan 236dcbfa04aSSaqib Khan // If there is no ubi volume for bmc version then read the /etc/os-release 237dcbfa04aSSaqib Khan // and create rofs-<versionId> under /media 238dcbfa04aSSaqib Khan if (activations.size() == 0) 239dcbfa04aSSaqib Khan { 240d16bcbd5SGunnar Mills auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE); 241dcbfa04aSSaqib Khan auto id = phosphor::software::manager::Version::getId(version); 242dcbfa04aSSaqib Khan auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/"; 243dcbfa04aSSaqib Khan try 244dcbfa04aSSaqib Khan { 245dcbfa04aSSaqib Khan if (!fs::is_directory(versionFileDir)) 246dcbfa04aSSaqib Khan { 247dcbfa04aSSaqib Khan fs::create_directories(versionFileDir); 248dcbfa04aSSaqib Khan } 249dcbfa04aSSaqib Khan auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE; 250dcbfa04aSSaqib Khan fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath); 251dcbfa04aSSaqib Khan ItemUpdater::processBMCImage(); 252dcbfa04aSSaqib Khan } 253dcbfa04aSSaqib Khan catch (const std::exception& e) 254dcbfa04aSSaqib Khan { 255dcbfa04aSSaqib Khan log<level::ERR>(e.what()); 256dcbfa04aSSaqib Khan } 257dcbfa04aSSaqib Khan } 258eaa1ee05SEddie James 259eaa1ee05SEddie James mirrorUbootToAlt(); 260ba239881SSaqib Khan return; 261ba239881SSaqib Khan } 262ba239881SSaqib Khan 2633526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId) 2643526ef73SLeonel Gonzalez { 2656d873715SEddie James // Find entry in versions map 2666d873715SEddie James auto it = versions.find(entryId); 2676d873715SEddie James if (it != versions.end()) 2686d873715SEddie James { 2696d873715SEddie James if (it->second->isFunctional()) 2706d873715SEddie James { 2712285fe0fSAdriana Kobylak log<level::ERR>("Error: Version is currently running on the BMC. " 2722285fe0fSAdriana Kobylak "Unable to remove.", 2732285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 2746d873715SEddie James return; 2756d873715SEddie James } 2766d873715SEddie James 2776d873715SEddie James // Delete ReadOnly partitions if it's not active 2783526ef73SLeonel Gonzalez removeReadOnlyPartition(entryId); 2791eef62deSSaqib Khan removeFile(entryId); 280ee13e831SSaqib Khan 281ee13e831SSaqib Khan // Removing entry in versions map 282ee13e831SSaqib Khan this->versions.erase(entryId); 2836d873715SEddie James } 2846d873715SEddie James else 2856d873715SEddie James { 2866d873715SEddie James // Delete ReadOnly partitions even if we can't find the version 2876d873715SEddie James removeReadOnlyPartition(entryId); 2886d873715SEddie James removeFile(entryId); 2896d873715SEddie James 2902285fe0fSAdriana Kobylak log<level::ERR>("Error: Failed to find version in item updater " 2912285fe0fSAdriana Kobylak "versions map. Unable to remove.", 2922285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 2936d873715SEddie James } 2941eef62deSSaqib Khan 2951eef62deSSaqib Khan // Remove the priority environment variable. 2961eef62deSSaqib Khan auto serviceFile = "obmc-flash-bmc-setenv@" + entryId + ".service"; 2972285fe0fSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 2982285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 2991eef62deSSaqib Khan method.append(serviceFile, "replace"); 3001eef62deSSaqib Khan bus.call_noreply(method); 3013526ef73SLeonel Gonzalez 3023526ef73SLeonel Gonzalez // Removing entry in activations map 3033526ef73SLeonel Gonzalez auto ita = activations.find(entryId); 3043526ef73SLeonel Gonzalez if (ita == activations.end()) 3053526ef73SLeonel Gonzalez { 3062285fe0fSAdriana Kobylak log<level::ERR>("Error: Failed to find version in item updater " 3072285fe0fSAdriana Kobylak "activations map. Unable to remove.", 3082285fe0fSAdriana Kobylak entry("VERSIONID=%s", entryId.c_str())); 3093526ef73SLeonel Gonzalez } 310ee13e831SSaqib Khan else 311ee13e831SSaqib Khan { 3123526ef73SLeonel Gonzalez this->activations.erase(entryId); 313ee13e831SSaqib Khan } 31449446ae9SSaqib Khan ItemUpdater::resetUbootEnvVars(); 315ee13e831SSaqib Khan return; 3163526ef73SLeonel Gonzalez } 3173526ef73SLeonel Gonzalez 318bc1bf3afSMichael Tritz void ItemUpdater::deleteAll() 319bc1bf3afSMichael Tritz { 32083cd21fbSAdriana Kobylak std::vector<std::string> deletableVersions; 32183cd21fbSAdriana Kobylak 322bc1bf3afSMichael Tritz for (const auto& versionIt : versions) 323bc1bf3afSMichael Tritz { 324bc1bf3afSMichael Tritz if (!versionIt.second->isFunctional()) 325bc1bf3afSMichael Tritz { 32683cd21fbSAdriana Kobylak deletableVersions.push_back(versionIt.first); 327bc1bf3afSMichael Tritz } 328bc1bf3afSMichael Tritz } 329bc1bf3afSMichael Tritz 33083cd21fbSAdriana Kobylak for (const auto& deletableIt : deletableVersions) 33183cd21fbSAdriana Kobylak { 33283cd21fbSAdriana Kobylak ItemUpdater::erase(deletableIt); 33383cd21fbSAdriana Kobylak } 33483cd21fbSAdriana Kobylak 335bc1bf3afSMichael Tritz // Remove any volumes that do not match current versions. 3362285fe0fSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 3372285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 338bc1bf3afSMichael Tritz method.append("obmc-flash-bmc-cleanup.service", "replace"); 339bc1bf3afSMichael Tritz bus.call_noreply(method); 340bc1bf3afSMichael Tritz } 341bc1bf3afSMichael Tritz 3422285fe0fSAdriana Kobylak ItemUpdater::ActivationStatus 3432285fe0fSAdriana Kobylak ItemUpdater::validateSquashFSImage(const std::string& filePath) 34435e83f3eSSaqib Khan { 345b1cfdf99SMichael Tritz bool invalid = false; 34635e83f3eSSaqib Khan 347b1cfdf99SMichael Tritz for (auto& bmcImage : bmcImages) 348b1cfdf99SMichael Tritz { 34919177d3eSSaqib Khan fs::path file(filePath); 35035e83f3eSSaqib Khan file /= bmcImage; 35135e83f3eSSaqib Khan std::ifstream efile(file.c_str()); 352b1cfdf99SMichael Tritz if (efile.good() != 1) 35335e83f3eSSaqib Khan { 354b1cfdf99SMichael Tritz log<level::ERR>("Failed to find the BMC image.", 355b1cfdf99SMichael Tritz entry("IMAGE=%s", bmcImage.c_str())); 356b1cfdf99SMichael Tritz invalid = true; 35735e83f3eSSaqib Khan } 358b1cfdf99SMichael Tritz } 359b1cfdf99SMichael Tritz 360b1cfdf99SMichael Tritz if (invalid) 36135e83f3eSSaqib Khan { 36235e83f3eSSaqib Khan return ItemUpdater::ActivationStatus::invalid; 36335e83f3eSSaqib Khan } 364b1cfdf99SMichael Tritz 365b1cfdf99SMichael Tritz return ItemUpdater::ActivationStatus::ready; 36635e83f3eSSaqib Khan } 36735e83f3eSSaqib Khan 368b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId) 3694c1aec09SSaqib Khan { 370b77551cdSAdriana Kobylak std::map<std::string, uint8_t> priorityMap; 371b77551cdSAdriana Kobylak 372b77551cdSAdriana Kobylak // Insert the requested version and priority, it may not exist yet. 373b77551cdSAdriana Kobylak priorityMap.insert(std::make_pair(versionId, value)); 374b77551cdSAdriana Kobylak 3754c1aec09SSaqib Khan for (const auto& intf : activations) 3764c1aec09SSaqib Khan { 3774c1aec09SSaqib Khan if (intf.second->redundancyPriority) 3784c1aec09SSaqib Khan { 379b77551cdSAdriana Kobylak priorityMap.insert(std::make_pair( 3802285fe0fSAdriana Kobylak intf.first, intf.second->redundancyPriority.get()->priority())); 381b77551cdSAdriana Kobylak } 382b77551cdSAdriana Kobylak } 383b77551cdSAdriana Kobylak 384b77551cdSAdriana Kobylak // Lambda function to compare 2 priority values, use <= to allow duplicates 3852285fe0fSAdriana Kobylak typedef std::function<bool(std::pair<std::string, uint8_t>, 3862285fe0fSAdriana Kobylak std::pair<std::string, uint8_t>)> 3872285fe0fSAdriana Kobylak cmpPriority; 3882285fe0fSAdriana Kobylak cmpPriority cmpPriorityFunc = 3892285fe0fSAdriana Kobylak [](std::pair<std::string, uint8_t> priority1, 3902285fe0fSAdriana Kobylak std::pair<std::string, uint8_t> priority2) { 391b77551cdSAdriana Kobylak return priority1.second <= priority2.second; 392b77551cdSAdriana Kobylak }; 393b77551cdSAdriana Kobylak 394b77551cdSAdriana Kobylak // Sort versions by ascending priority 395b77551cdSAdriana Kobylak std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet( 396b77551cdSAdriana Kobylak priorityMap.begin(), priorityMap.end(), cmpPriorityFunc); 397b77551cdSAdriana Kobylak 398b77551cdSAdriana Kobylak auto freePriorityValue = value; 399b77551cdSAdriana Kobylak for (auto& element : prioritySet) 400b77551cdSAdriana Kobylak { 401b77551cdSAdriana Kobylak if (element.first == versionId) 402b77551cdSAdriana Kobylak { 403b77551cdSAdriana Kobylak continue; 404b77551cdSAdriana Kobylak } 405b77551cdSAdriana Kobylak if (element.second == freePriorityValue) 406b77551cdSAdriana Kobylak { 407b77551cdSAdriana Kobylak ++freePriorityValue; 408b77551cdSAdriana Kobylak auto it = activations.find(element.first); 409b77551cdSAdriana Kobylak it->second->redundancyPriority.get()->sdbusPriority( 410b77551cdSAdriana Kobylak freePriorityValue); 4114c1aec09SSaqib Khan } 4124c1aec09SSaqib Khan } 413b77551cdSAdriana Kobylak 414b77551cdSAdriana Kobylak auto lowestVersion = prioritySet.begin()->first; 415b77551cdSAdriana Kobylak if (value == prioritySet.begin()->second) 416b77551cdSAdriana Kobylak { 417b77551cdSAdriana Kobylak lowestVersion = versionId; 4184c1aec09SSaqib Khan } 419b77551cdSAdriana Kobylak updateUbootEnvVars(lowestVersion); 4204c1aec09SSaqib Khan } 4214c1aec09SSaqib Khan 42237a59043SMichael Tritz void ItemUpdater::reset() 42337a59043SMichael Tritz { 42437a59043SMichael Tritz // Mark the read-write partition for recreation upon reboot. 4252285fe0fSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 4262285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 4270129d926SMichael Tritz method.append("obmc-flash-bmc-setenv@rwreset\\x3dtrue.service", "replace"); 42837a59043SMichael Tritz bus.call_noreply(method); 42937a59043SMichael Tritz 43037a59043SMichael Tritz log<level::INFO>("BMC factory reset will take effect upon reboot."); 43137a59043SMichael Tritz 43237a59043SMichael Tritz return; 43337a59043SMichael Tritz } 43437a59043SMichael Tritz 4353526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId) 4363526ef73SLeonel Gonzalez { 4372285fe0fSAdriana Kobylak auto serviceFile = "obmc-flash-bmc-ubiro-remove@" + versionId + ".service"; 4383526ef73SLeonel Gonzalez 4393526ef73SLeonel Gonzalez // Remove the read-only partitions. 4402285fe0fSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 4412285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 4423526ef73SLeonel Gonzalez method.append(serviceFile, "replace"); 4433526ef73SLeonel Gonzalez bus.call_noreply(method); 4443526ef73SLeonel Gonzalez } 4453526ef73SLeonel Gonzalez 4460129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value) 4470129d926SMichael Tritz { 4480129d926SMichael Tritz // enabling field mode is intended to be one way: false -> true 4490129d926SMichael Tritz if (value && !control::FieldMode::fieldModeEnabled()) 4500129d926SMichael Tritz { 4510129d926SMichael Tritz control::FieldMode::fieldModeEnabled(value); 4520129d926SMichael Tritz 4532285fe0fSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 4542285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 4550129d926SMichael Tritz method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service", 4560129d926SMichael Tritz "replace"); 4570129d926SMichael Tritz bus.call_noreply(method); 4580129d926SMichael Tritz 4592285fe0fSAdriana Kobylak method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 4602285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StopUnit"); 4610129d926SMichael Tritz method.append("usr-local.mount", "replace"); 4620129d926SMichael Tritz bus.call_noreply(method); 4630129d926SMichael Tritz 4640129d926SMichael Tritz std::vector<std::string> usrLocal = {"usr-local.mount"}; 4650129d926SMichael Tritz 4662285fe0fSAdriana Kobylak method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 4672285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "MaskUnitFiles"); 4680129d926SMichael Tritz method.append(usrLocal, false, true); 4690129d926SMichael Tritz bus.call_noreply(method); 4700129d926SMichael Tritz } 4710129d926SMichael Tritz 4720129d926SMichael Tritz return control::FieldMode::fieldModeEnabled(); 4730129d926SMichael Tritz } 4740129d926SMichael Tritz 4750129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus() 4760129d926SMichael Tritz { 477ff0b421dSMichael Tritz std::ifstream input("/dev/mtd/u-boot-env"); 4780129d926SMichael Tritz std::string envVar; 4790129d926SMichael Tritz std::getline(input, envVar); 4800129d926SMichael Tritz 4810129d926SMichael Tritz if (envVar.find("fieldmode=true") != std::string::npos) 4820129d926SMichael Tritz { 4830129d926SMichael Tritz ItemUpdater::fieldModeEnabled(true); 4840129d926SMichael Tritz } 4850129d926SMichael Tritz } 4860129d926SMichael Tritz 487b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath() 488b60add1eSGunnar Mills { 489b60add1eSGunnar Mills auto depth = 0; 4902285fe0fSAdriana Kobylak auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 4912285fe0fSAdriana Kobylak MAPPER_INTERFACE, "GetSubTreePaths"); 492b60add1eSGunnar Mills 4931254c628SAdriana Kobylak mapperCall.append(INVENTORY_PATH); 494b60add1eSGunnar Mills mapperCall.append(depth); 4951254c628SAdriana Kobylak std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE}; 496b60add1eSGunnar Mills mapperCall.append(filter); 497b60add1eSGunnar Mills 498b60add1eSGunnar Mills auto response = bus.call(mapperCall); 499b60add1eSGunnar Mills if (response.is_method_error()) 500b60add1eSGunnar Mills { 501b60add1eSGunnar Mills log<level::ERR>("Error in mapper GetSubTreePath"); 502b60add1eSGunnar Mills return; 503b60add1eSGunnar Mills } 504b60add1eSGunnar Mills 505b60add1eSGunnar Mills using ObjectPaths = std::vector<std::string>; 506b60add1eSGunnar Mills ObjectPaths result; 507b60add1eSGunnar Mills response.read(result); 508b60add1eSGunnar Mills 5091254c628SAdriana Kobylak if (!result.empty()) 510b60add1eSGunnar Mills { 5111254c628SAdriana Kobylak bmcInventoryPath = result.front(); 512b60add1eSGunnar Mills } 513b60add1eSGunnar Mills 514b60add1eSGunnar Mills return; 515b60add1eSGunnar Mills } 516b60add1eSGunnar Mills 517f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path) 518ded875dcSGunnar Mills { 5192285fe0fSAdriana Kobylak assocs.emplace_back( 5202285fe0fSAdriana Kobylak std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path)); 521ded875dcSGunnar Mills associations(assocs); 522ded875dcSGunnar Mills } 523ded875dcSGunnar Mills 52488e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path) 52588e8a325SGunnar Mills { 52688e8a325SGunnar Mills assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION, 5272285fe0fSAdriana Kobylak FUNCTIONAL_REV_ASSOCIATION, path)); 52888e8a325SGunnar Mills associations(assocs); 52988e8a325SGunnar Mills } 53088e8a325SGunnar Mills 531f10b2326SGunnar Mills void ItemUpdater::removeActiveAssociation(const std::string& path) 532ded875dcSGunnar Mills { 533ded875dcSGunnar Mills for (auto iter = assocs.begin(); iter != assocs.end();) 534ded875dcSGunnar Mills { 53588e8a325SGunnar Mills // Since there could be multiple associations to the same path, 53688e8a325SGunnar Mills // only remove ones that have an active forward association. 53788e8a325SGunnar Mills if ((std::get<0>(*iter)).compare(ACTIVE_FWD_ASSOCIATION) == 0 && 53888e8a325SGunnar Mills (std::get<2>(*iter)).compare(path) == 0) 539ded875dcSGunnar Mills { 540ded875dcSGunnar Mills iter = assocs.erase(iter); 541ded875dcSGunnar Mills associations(assocs); 542ded875dcSGunnar Mills } 543ded875dcSGunnar Mills else 544ded875dcSGunnar Mills { 545ded875dcSGunnar Mills ++iter; 546ded875dcSGunnar Mills } 547ded875dcSGunnar Mills } 548ded875dcSGunnar Mills } 549ded875dcSGunnar Mills 550b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value) 551b9da6634SSaqib Khan { 552b9da6634SSaqib Khan for (const auto& intf : activations) 553b9da6634SSaqib Khan { 554b9da6634SSaqib Khan if (intf.second->redundancyPriority) 555b9da6634SSaqib Khan { 556b9da6634SSaqib Khan if (intf.second->redundancyPriority.get()->priority() < value) 557b9da6634SSaqib Khan { 558b9da6634SSaqib Khan return false; 559b9da6634SSaqib Khan } 560b9da6634SSaqib Khan } 561b9da6634SSaqib Khan } 562b9da6634SSaqib Khan return true; 563b9da6634SSaqib Khan } 564b9da6634SSaqib Khan 565b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId) 566b77551cdSAdriana Kobylak { 5672285fe0fSAdriana Kobylak auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 5682285fe0fSAdriana Kobylak SYSTEMD_INTERFACE, "StartUnit"); 5692285fe0fSAdriana Kobylak auto updateEnvVarsFile = 5702285fe0fSAdriana Kobylak "obmc-flash-bmc-updateubootvars@" + versionId + ".service"; 571b77551cdSAdriana Kobylak method.append(updateEnvVarsFile, "replace"); 572b77551cdSAdriana Kobylak auto result = bus.call(method); 573b77551cdSAdriana Kobylak 574b77551cdSAdriana Kobylak // Check that the bus call didn't result in an error 575b77551cdSAdriana Kobylak if (result.is_method_error()) 576b77551cdSAdriana Kobylak { 577b77551cdSAdriana Kobylak log<level::ERR>("Failed to update u-boot env variables", 578596466b8SAdriana Kobylak entry("VERSIONID=%s", versionId.c_str())); 579b77551cdSAdriana Kobylak } 580b77551cdSAdriana Kobylak } 581b77551cdSAdriana Kobylak 58249446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars() 58349446ae9SSaqib Khan { 58449446ae9SSaqib Khan decltype(activations.begin()->second->redundancyPriority.get()->priority()) 58549446ae9SSaqib Khan lowestPriority = std::numeric_limits<uint8_t>::max(); 58649446ae9SSaqib Khan decltype(activations.begin()->second->versionId) lowestPriorityVersion; 58749446ae9SSaqib Khan for (const auto& intf : activations) 58849446ae9SSaqib Khan { 58949446ae9SSaqib Khan if (!intf.second->redundancyPriority.get()) 59049446ae9SSaqib Khan { 59149446ae9SSaqib Khan // Skip this version if the redundancyPriority is not initialized. 59249446ae9SSaqib Khan continue; 59349446ae9SSaqib Khan } 59449446ae9SSaqib Khan 5952285fe0fSAdriana Kobylak if (intf.second->redundancyPriority.get()->priority() <= lowestPriority) 59649446ae9SSaqib Khan { 59749446ae9SSaqib Khan lowestPriority = intf.second->redundancyPriority.get()->priority(); 59849446ae9SSaqib Khan lowestPriorityVersion = intf.second->versionId; 59949446ae9SSaqib Khan } 60049446ae9SSaqib Khan } 60149446ae9SSaqib Khan 602f0382c35SSaqib Khan // Update the U-boot environment variable to point to the lowest priority 603b77551cdSAdriana Kobylak updateUbootEnvVars(lowestPriorityVersion); 60449446ae9SSaqib Khan } 60549446ae9SSaqib Khan 606204e1e74SAdriana Kobylak void ItemUpdater::freeSpace() 607204e1e74SAdriana Kobylak { 608204e1e74SAdriana Kobylak // Versions with the highest priority in front 609204e1e74SAdriana Kobylak std::priority_queue<std::pair<int, std::string>, 610204e1e74SAdriana Kobylak std::vector<std::pair<int, std::string>>, 6112285fe0fSAdriana Kobylak std::less<std::pair<int, std::string>>> 6122285fe0fSAdriana Kobylak versionsPQ; 613204e1e74SAdriana Kobylak 614204e1e74SAdriana Kobylak std::size_t count = 0; 615204e1e74SAdriana Kobylak for (const auto& iter : activations) 616204e1e74SAdriana Kobylak { 617204e1e74SAdriana Kobylak if ((iter.second.get()->activation() == 618204e1e74SAdriana Kobylak server::Activation::Activations::Active) || 619204e1e74SAdriana Kobylak (iter.second.get()->activation() == 620204e1e74SAdriana Kobylak server::Activation::Activations::Failed)) 621204e1e74SAdriana Kobylak { 622204e1e74SAdriana Kobylak count++; 623204e1e74SAdriana Kobylak // Don't put the functional version on the queue since we can't 624204e1e74SAdriana Kobylak // remove the "running" BMC version. 625204e1e74SAdriana Kobylak if (versions.find(iter.second->versionId)->second->isFunctional()) 626204e1e74SAdriana Kobylak { 627204e1e74SAdriana Kobylak continue; 628204e1e74SAdriana Kobylak } 629204e1e74SAdriana Kobylak versionsPQ.push(std::make_pair( 630204e1e74SAdriana Kobylak iter.second->redundancyPriority.get()->priority(), 631204e1e74SAdriana Kobylak iter.second->versionId)); 632204e1e74SAdriana Kobylak } 633204e1e74SAdriana Kobylak } 634204e1e74SAdriana Kobylak 635204e1e74SAdriana Kobylak // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1, 636204e1e74SAdriana Kobylak // remove the highest priority one(s). 637204e1e74SAdriana Kobylak while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty())) 638204e1e74SAdriana Kobylak { 639204e1e74SAdriana Kobylak erase(versionsPQ.top().second); 640204e1e74SAdriana Kobylak versionsPQ.pop(); 641204e1e74SAdriana Kobylak count--; 642204e1e74SAdriana Kobylak } 643204e1e74SAdriana Kobylak } 644204e1e74SAdriana Kobylak 645eaa1ee05SEddie James void ItemUpdater::mirrorUbootToAlt() 646eaa1ee05SEddie James { 647eaa1ee05SEddie James auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 648eaa1ee05SEddie James SYSTEMD_INTERFACE, "StartUnit"); 649eaa1ee05SEddie James auto mirrorUbootFile = "obmc-flash-bmc-mirroruboot.service"; 650eaa1ee05SEddie James method.append(mirrorUbootFile, "replace"); 651eaa1ee05SEddie James auto result = bus.call(method); 652eaa1ee05SEddie James 653eaa1ee05SEddie James // Check that the bus call didn't result in an error 654eaa1ee05SEddie James if (result.is_method_error()) 655eaa1ee05SEddie James { 656eaa1ee05SEddie James log<level::ERR>("Failed to copy U-Boot to alternate chip"); 657eaa1ee05SEddie James } 658eaa1ee05SEddie James } 659eaa1ee05SEddie James 660ec1b41c4SGunnar Mills } // namespace updater 661ec1b41c4SGunnar Mills } // namespace software 662ec1b41c4SGunnar Mills } // namespace phosphor 663