135e83f3eSSaqib Khan #include <fstream> 2b77551cdSAdriana Kobylak #include <set> 3ec1b41c4SGunnar Mills #include <string> 42ce7da29SGunnar Mills #include <phosphor-logging/log.hpp> 5dcbfa04aSSaqib Khan #include <phosphor-logging/elog.hpp> 6dcbfa04aSSaqib Khan #include <elog-errors.hpp> 7dcbfa04aSSaqib Khan #include <xyz/openbmc_project/Software/Version/error.hpp> 8ec1b41c4SGunnar Mills #include "config.h" 92ce7da29SGunnar Mills #include "item_updater.hpp" 102ce7da29SGunnar Mills #include "xyz/openbmc_project/Software/Version/server.hpp" 1135e83f3eSSaqib Khan #include <experimental/filesystem> 12705f1bfcSSaqib Khan #include "version.hpp" 135d532675SSaqib Khan #include "serialize.hpp" 14ec1b41c4SGunnar Mills 15ec1b41c4SGunnar Mills namespace phosphor 16ec1b41c4SGunnar Mills { 17ec1b41c4SGunnar Mills namespace software 18ec1b41c4SGunnar Mills { 19ec1b41c4SGunnar Mills namespace updater 20ec1b41c4SGunnar Mills { 21ec1b41c4SGunnar Mills 222ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class 232ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server; 240129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server; 252ce7da29SGunnar Mills 262ce7da29SGunnar Mills using namespace phosphor::logging; 27dcbfa04aSSaqib Khan using namespace sdbusplus::xyz::openbmc_project::Software::Version::Error; 2835e83f3eSSaqib Khan namespace fs = std::experimental::filesystem; 2935e83f3eSSaqib Khan 30b1cfdf99SMichael Tritz const std::vector<std::string> bmcImages = { "image-kernel", 31b1cfdf99SMichael Tritz "image-rofs", 32b1cfdf99SMichael Tritz "image-rwfs", 33b1cfdf99SMichael Tritz "image-u-boot" }; 342ce7da29SGunnar Mills 35e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg) 36ec1b41c4SGunnar Mills { 3784a0e693SSaqib Khan 3884a0e693SSaqib Khan using SVersion = server::Version; 3984a0e693SSaqib Khan using VersionPurpose = SVersion::VersionPurpose; 409a782243SGunnar Mills using VersionClass = phosphor::software::manager::Version; 4184a0e693SSaqib Khan namespace mesg = sdbusplus::message; 4284a0e693SSaqib Khan namespace variant_ns = mesg::variant_ns; 4384a0e693SSaqib Khan 4484a0e693SSaqib Khan mesg::object_path objPath; 4584a0e693SSaqib Khan auto purpose = VersionPurpose::Unknown; 46705f1bfcSSaqib Khan std::string version; 472ce7da29SGunnar Mills std::map<std::string, 482ce7da29SGunnar Mills std::map<std::string, 4984a0e693SSaqib Khan mesg::variant<std::string>>> interfaces; 50e75d10f5SPatrick Williams msg.read(objPath, interfaces); 512ce7da29SGunnar Mills std::string path(std::move(objPath)); 5219177d3eSSaqib Khan std::string filePath; 532ce7da29SGunnar Mills 542ce7da29SGunnar Mills for (const auto& intf : interfaces) 552ce7da29SGunnar Mills { 56705f1bfcSSaqib Khan if (intf.first == VERSION_IFACE) 572ce7da29SGunnar Mills { 582ce7da29SGunnar Mills for (const auto& property : intf.second) 592ce7da29SGunnar Mills { 60705f1bfcSSaqib Khan if (property.first == "Purpose") 612ce7da29SGunnar Mills { 6284a0e693SSaqib Khan auto value = SVersion::convertVersionPurposeFromString( 6384a0e693SSaqib Khan variant_ns::get<std::string>(property.second)); 6484a0e693SSaqib Khan if (value == VersionPurpose::BMC || 6584a0e693SSaqib Khan value == VersionPurpose::System) 6684a0e693SSaqib Khan { 6784a0e693SSaqib Khan purpose = value; 6884a0e693SSaqib Khan } 69705f1bfcSSaqib Khan } 70705f1bfcSSaqib Khan else if (property.first == "Version") 71705f1bfcSSaqib Khan { 7284a0e693SSaqib Khan version = variant_ns::get<std::string>(property.second); 73705f1bfcSSaqib Khan } 74705f1bfcSSaqib Khan } 75705f1bfcSSaqib Khan } 7619177d3eSSaqib Khan else if (intf.first == FILEPATH_IFACE) 7719177d3eSSaqib Khan { 7819177d3eSSaqib Khan for (const auto& property : intf.second) 7919177d3eSSaqib Khan { 8019177d3eSSaqib Khan if (property.first == "Path") 8119177d3eSSaqib Khan { 8284a0e693SSaqib Khan filePath = variant_ns::get<std::string>(property.second); 8319177d3eSSaqib Khan } 8419177d3eSSaqib Khan } 8519177d3eSSaqib Khan } 86705f1bfcSSaqib Khan } 87705f1bfcSSaqib Khan if (version.empty() || 8819177d3eSSaqib Khan filePath.empty() || 8984a0e693SSaqib Khan purpose == VersionPurpose::Unknown) 902ce7da29SGunnar Mills { 91e75d10f5SPatrick Williams return; 922ce7da29SGunnar Mills } 932ce7da29SGunnar Mills 942ce7da29SGunnar Mills // Version id is the last item in the path 952ce7da29SGunnar Mills auto pos = path.rfind("/"); 962ce7da29SGunnar Mills if (pos == std::string::npos) 972ce7da29SGunnar Mills { 982ce7da29SGunnar Mills log<level::ERR>("No version id found in object path", 992ce7da29SGunnar Mills entry("OBJPATH=%s", path)); 100e75d10f5SPatrick Williams return; 1012ce7da29SGunnar Mills } 1022ce7da29SGunnar Mills 1032ce7da29SGunnar Mills auto versionId = path.substr(pos + 1); 1042ce7da29SGunnar Mills 105e75d10f5SPatrick Williams if (activations.find(versionId) == activations.end()) 1062ce7da29SGunnar Mills { 10735e83f3eSSaqib Khan // Determine the Activation state by processing the given image dir. 10835e83f3eSSaqib Khan auto activationState = server::Activation::Activations::Invalid; 1099a782243SGunnar Mills ItemUpdater::ActivationStatus result = 1109a782243SGunnar Mills ItemUpdater::validateSquashFSImage(filePath); 11143b25cdeSGunnar Mills AssociationList associations = {}; 11243b25cdeSGunnar Mills 11335e83f3eSSaqib Khan if (result == ItemUpdater::ActivationStatus::ready) 11435e83f3eSSaqib Khan { 11535e83f3eSSaqib Khan activationState = server::Activation::Activations::Ready; 116b60add1eSGunnar Mills // Create an association to the BMC inventory item 11743b25cdeSGunnar Mills associations.emplace_back(std::make_tuple( 118b60add1eSGunnar Mills ACTIVATION_FWD_ASSOCIATION, 119b60add1eSGunnar Mills ACTIVATION_REV_ASSOCIATION, 12043b25cdeSGunnar Mills bmcInventoryPath)); 12143b25cdeSGunnar Mills } 122b60add1eSGunnar Mills 123ee13e831SSaqib Khan activations.insert(std::make_pair( 124ee13e831SSaqib Khan versionId, 125ee13e831SSaqib Khan std::make_unique<Activation>( 12635e83f3eSSaqib Khan bus, 12735e83f3eSSaqib Khan path, 1284c1aec09SSaqib Khan *this, 12935e83f3eSSaqib Khan versionId, 130b60add1eSGunnar Mills activationState, 131ee13e831SSaqib Khan associations))); 1324254beceSMichael Tritz 133ee13e831SSaqib Khan auto versionPtr = std::make_unique<VersionClass>( 134705f1bfcSSaqib Khan bus, 135705f1bfcSSaqib Khan path, 136705f1bfcSSaqib Khan version, 137705f1bfcSSaqib Khan purpose, 138ee13e831SSaqib Khan filePath, 139ee13e831SSaqib Khan std::bind(&ItemUpdater::erase, 140ee13e831SSaqib Khan this, 141ee13e831SSaqib Khan std::placeholders::_1)); 142ee13e831SSaqib Khan versionPtr->deleteObject = 143ee13e831SSaqib Khan std::make_unique<phosphor::software::manager::Delete>( 144ee13e831SSaqib Khan bus, path, *versionPtr); 145ee13e831SSaqib Khan versions.insert(std::make_pair(versionId, std::move(versionPtr))); 1462ce7da29SGunnar Mills } 147e75d10f5SPatrick Williams return; 148ec1b41c4SGunnar Mills } 149ec1b41c4SGunnar Mills 150ba239881SSaqib Khan void ItemUpdater::processBMCImage() 151ba239881SSaqib Khan { 15288e8a325SGunnar Mills using VersionClass = phosphor::software::manager::Version; 15388e8a325SGunnar Mills // Read os-release from /etc/ to get the functional BMC version 15488e8a325SGunnar Mills auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE); 15588e8a325SGunnar Mills 1561eef62deSSaqib Khan // Read os-release from folders under /media/ to get 1571eef62deSSaqib Khan // BMC Software Versions. 1581eef62deSSaqib Khan for (const auto& iter : fs::directory_iterator(MEDIA_DIR)) 1591eef62deSSaqib Khan { 1601eef62deSSaqib Khan auto activationState = server::Activation::Activations::Active; 1616fab70daSSaqib Khan static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX); 1621eef62deSSaqib Khan 1631eef62deSSaqib Khan // Check if the BMC_RO_PREFIXis the prefix of the iter.path 1641eef62deSSaqib Khan if (0 == iter.path().native().compare(0, BMC_RO_PREFIX_LEN, 1656fab70daSSaqib Khan BMC_ROFS_PREFIX)) 1661eef62deSSaqib Khan { 167021c365bSSaqib Khan // The versionId is extracted from the path 168021c365bSSaqib Khan // for example /media/ro-2a1022fe. 169021c365bSSaqib Khan auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN); 1701eef62deSSaqib Khan auto osRelease = iter.path() / OS_RELEASE_FILE; 1711eef62deSSaqib Khan if (!fs::is_regular_file(osRelease)) 1721eef62deSSaqib Khan { 1732ad1b55fSGunnar Mills log<level::ERR>("Failed to read osRelease", 1742ad1b55fSGunnar Mills entry("FILENAME=%s", osRelease.string())); 175021c365bSSaqib Khan ItemUpdater::erase(id); 176021c365bSSaqib Khan continue; 1771eef62deSSaqib Khan } 17888e8a325SGunnar Mills auto version = VersionClass::getBMCVersion(osRelease); 1791eef62deSSaqib Khan if (version.empty()) 1801eef62deSSaqib Khan { 1811eef62deSSaqib Khan log<level::ERR>("Failed to read version from osRelease", 1821eef62deSSaqib Khan entry("FILENAME=%s", osRelease.string())); 1831eef62deSSaqib Khan activationState = server::Activation::Activations::Invalid; 1841eef62deSSaqib Khan } 185021c365bSSaqib Khan 1861eef62deSSaqib Khan auto purpose = server::Version::VersionPurpose::BMC; 1871eef62deSSaqib Khan auto path = fs::path(SOFTWARE_OBJPATH) / id; 1881eef62deSSaqib Khan 18988e8a325SGunnar Mills // Create functional association if this is the functional version 19088e8a325SGunnar Mills if (version.compare(functionalVersion) == 0) 19188e8a325SGunnar Mills { 19288e8a325SGunnar Mills createFunctionalAssociation(path); 19388e8a325SGunnar Mills } 19488e8a325SGunnar Mills 19543b25cdeSGunnar Mills AssociationList associations = {}; 19643b25cdeSGunnar Mills 19743b25cdeSGunnar Mills if (activationState == server::Activation::Activations::Active) 19843b25cdeSGunnar Mills { 19943b25cdeSGunnar Mills // Create an association to the BMC inventory item 20043b25cdeSGunnar Mills associations.emplace_back(std::make_tuple( 20143b25cdeSGunnar Mills ACTIVATION_FWD_ASSOCIATION, 20243b25cdeSGunnar Mills ACTIVATION_REV_ASSOCIATION, 20343b25cdeSGunnar Mills bmcInventoryPath)); 20443b25cdeSGunnar Mills 20543b25cdeSGunnar Mills // Create an active association since this image is active 20643b25cdeSGunnar Mills createActiveAssociation(path); 20743b25cdeSGunnar Mills } 20843b25cdeSGunnar Mills 209ee590c74SAdriana Kobylak // Create Version instance for this version. 210ee590c74SAdriana Kobylak auto versionPtr = std::make_unique<VersionClass>( 211ee590c74SAdriana Kobylak bus, 212ee590c74SAdriana Kobylak path, 213ee590c74SAdriana Kobylak version, 214ee590c74SAdriana Kobylak purpose, 215ee13e831SSaqib Khan "", 216ee13e831SSaqib Khan std::bind(&ItemUpdater::erase, 217ee13e831SSaqib Khan this, 218ee13e831SSaqib Khan 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 } 226ee590c74SAdriana Kobylak versions.insert(std::make_pair( 227ee590c74SAdriana Kobylak id, 228ee590c74SAdriana Kobylak std::move(versionPtr))); 229ee590c74SAdriana Kobylak 2301eef62deSSaqib Khan // Create Activation instance for this version. 231ee13e831SSaqib Khan activations.insert(std::make_pair( 232ee13e831SSaqib Khan id, 233ee13e831SSaqib Khan std::make_unique<Activation>( 234ba239881SSaqib Khan bus, 235ba239881SSaqib Khan path, 2364c1aec09SSaqib Khan *this, 237ba239881SSaqib Khan id, 2384254beceSMichael Tritz activationState, 239ee13e831SSaqib Khan associations))); 2401eef62deSSaqib Khan 2411eef62deSSaqib Khan // If Active, create RedundancyPriority instance for this version. 2421eef62deSSaqib Khan if (activationState == server::Activation::Activations::Active) 2431eef62deSSaqib Khan { 2441eef62deSSaqib Khan uint8_t priority = std::numeric_limits<uint8_t>::max(); 2451eef62deSSaqib Khan if (!restoreFromFile(id, priority)) 2461eef62deSSaqib Khan { 247ee590c74SAdriana Kobylak if (isVersionFunctional) 248ee590c74SAdriana Kobylak { 249ee590c74SAdriana Kobylak priority = 0; 250ee590c74SAdriana Kobylak } 251ee590c74SAdriana Kobylak else 252ee590c74SAdriana Kobylak { 2531eef62deSSaqib Khan log<level::ERR>("Unable to restore priority from file.", 2541eef62deSSaqib Khan entry("VERSIONID=%s", id)); 2551eef62deSSaqib Khan } 256ee590c74SAdriana Kobylak } 2571eef62deSSaqib Khan activations.find(id)->second->redundancyPriority = 2581eef62deSSaqib Khan std::make_unique<RedundancyPriority>( 2591eef62deSSaqib Khan bus, 2601eef62deSSaqib Khan path, 2611eef62deSSaqib Khan *(activations.find(id)->second), 262b77551cdSAdriana Kobylak priority, 263b77551cdSAdriana Kobylak false); 2641eef62deSSaqib Khan } 2651eef62deSSaqib Khan } 2661eef62deSSaqib Khan } 267dcbfa04aSSaqib Khan 268dcbfa04aSSaqib Khan // If there is no ubi volume for bmc version then read the /etc/os-release 269dcbfa04aSSaqib Khan // and create rofs-<versionId> under /media 270dcbfa04aSSaqib Khan if (activations.size() == 0) 271dcbfa04aSSaqib Khan { 272d16bcbd5SGunnar Mills auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE); 273dcbfa04aSSaqib Khan auto id = phosphor::software::manager::Version::getId(version); 274dcbfa04aSSaqib Khan auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/"; 275dcbfa04aSSaqib Khan try 276dcbfa04aSSaqib Khan { 277dcbfa04aSSaqib Khan if (!fs::is_directory(versionFileDir)) 278dcbfa04aSSaqib Khan { 279dcbfa04aSSaqib Khan fs::create_directories(versionFileDir); 280dcbfa04aSSaqib Khan } 281dcbfa04aSSaqib Khan auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE; 282dcbfa04aSSaqib Khan fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath); 283dcbfa04aSSaqib Khan ItemUpdater::processBMCImage(); 284dcbfa04aSSaqib Khan } 285dcbfa04aSSaqib Khan catch (const std::exception& e) 286dcbfa04aSSaqib Khan { 287dcbfa04aSSaqib Khan log<level::ERR>(e.what()); 288dcbfa04aSSaqib Khan } 289dcbfa04aSSaqib Khan } 290ba239881SSaqib Khan return; 291ba239881SSaqib Khan } 292ba239881SSaqib Khan 2933526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId) 2943526ef73SLeonel Gonzalez { 2956d873715SEddie James // Find entry in versions map 2966d873715SEddie James auto it = versions.find(entryId); 2976d873715SEddie James if (it != versions.end()) 2986d873715SEddie James { 2996d873715SEddie James if (it->second->isFunctional()) 3006d873715SEddie James { 3016d873715SEddie James log<level::ERR>(("Error: Version " + entryId + \ 3026d873715SEddie James " is currently running on the BMC." \ 3036d873715SEddie James " Unable to remove.").c_str()); 3046d873715SEddie James return; 3056d873715SEddie James } 3066d873715SEddie James 3076d873715SEddie James // Delete ReadOnly partitions if it's not active 3083526ef73SLeonel Gonzalez removeReadOnlyPartition(entryId); 3091eef62deSSaqib Khan removeFile(entryId); 310ee13e831SSaqib Khan 311ee13e831SSaqib Khan // Removing entry in versions map 312ee13e831SSaqib Khan this->versions.erase(entryId); 3136d873715SEddie James } 3146d873715SEddie James else 3156d873715SEddie James { 3166d873715SEddie James // Delete ReadOnly partitions even if we can't find the version 3176d873715SEddie James removeReadOnlyPartition(entryId); 3186d873715SEddie James removeFile(entryId); 3196d873715SEddie James 3206d873715SEddie James log<level::ERR>(("Error: Failed to find version " + entryId + \ 3216d873715SEddie James " in item updater versions map." \ 3226d873715SEddie James " Unable to remove.").c_str()); 3236d873715SEddie James } 3241eef62deSSaqib Khan 3251eef62deSSaqib Khan // Remove the priority environment variable. 3261eef62deSSaqib Khan auto serviceFile = "obmc-flash-bmc-setenv@" + entryId + ".service"; 3271eef62deSSaqib Khan auto method = bus.new_method_call( 3281eef62deSSaqib Khan SYSTEMD_BUSNAME, 3291eef62deSSaqib Khan SYSTEMD_PATH, 3301eef62deSSaqib Khan SYSTEMD_INTERFACE, 3311eef62deSSaqib Khan "StartUnit"); 3321eef62deSSaqib Khan method.append(serviceFile, "replace"); 3331eef62deSSaqib Khan bus.call_noreply(method); 3343526ef73SLeonel Gonzalez 3353526ef73SLeonel Gonzalez // Removing entry in activations map 3363526ef73SLeonel Gonzalez auto ita = activations.find(entryId); 3373526ef73SLeonel Gonzalez if (ita == activations.end()) 3383526ef73SLeonel Gonzalez { 3393526ef73SLeonel Gonzalez log<level::ERR>(("Error: Failed to find version " + entryId + \ 3403526ef73SLeonel Gonzalez " in item updater activations map." \ 3413526ef73SLeonel Gonzalez " Unable to remove.").c_str()); 3423526ef73SLeonel Gonzalez } 343ee13e831SSaqib Khan else 344ee13e831SSaqib Khan { 3453526ef73SLeonel Gonzalez this->activations.erase(entryId); 346ee13e831SSaqib Khan } 34749446ae9SSaqib Khan ItemUpdater::resetUbootEnvVars(); 348ee13e831SSaqib Khan return; 3493526ef73SLeonel Gonzalez } 3503526ef73SLeonel Gonzalez 351bc1bf3afSMichael Tritz void ItemUpdater::deleteAll() 352bc1bf3afSMichael Tritz { 353bc1bf3afSMichael Tritz for (const auto& versionIt : versions) 354bc1bf3afSMichael Tritz { 355bc1bf3afSMichael Tritz if (!versionIt.second->isFunctional()) 356bc1bf3afSMichael Tritz { 357ee13e831SSaqib Khan ItemUpdater::erase(versionIt.first); 358bc1bf3afSMichael Tritz } 359bc1bf3afSMichael Tritz } 360bc1bf3afSMichael Tritz 361bc1bf3afSMichael Tritz // Remove any volumes that do not match current versions. 362bc1bf3afSMichael Tritz auto method = bus.new_method_call( 363bc1bf3afSMichael Tritz SYSTEMD_BUSNAME, 364bc1bf3afSMichael Tritz SYSTEMD_PATH, 365bc1bf3afSMichael Tritz SYSTEMD_INTERFACE, 366bc1bf3afSMichael Tritz "StartUnit"); 367bc1bf3afSMichael Tritz method.append("obmc-flash-bmc-cleanup.service", "replace"); 368bc1bf3afSMichael Tritz bus.call_noreply(method); 369bc1bf3afSMichael Tritz } 370bc1bf3afSMichael Tritz 37135e83f3eSSaqib Khan ItemUpdater::ActivationStatus ItemUpdater::validateSquashFSImage( 37219177d3eSSaqib Khan const std::string& filePath) 37335e83f3eSSaqib Khan { 374b1cfdf99SMichael Tritz bool invalid = false; 37535e83f3eSSaqib Khan 376b1cfdf99SMichael Tritz for (auto& bmcImage : bmcImages) 377b1cfdf99SMichael Tritz { 37819177d3eSSaqib Khan fs::path file(filePath); 37935e83f3eSSaqib Khan file /= bmcImage; 38035e83f3eSSaqib Khan std::ifstream efile(file.c_str()); 381b1cfdf99SMichael Tritz if (efile.good() != 1) 38235e83f3eSSaqib Khan { 383b1cfdf99SMichael Tritz log<level::ERR>("Failed to find the BMC image.", 384b1cfdf99SMichael Tritz entry("IMAGE=%s", bmcImage.c_str())); 385b1cfdf99SMichael Tritz invalid = true; 38635e83f3eSSaqib Khan } 387b1cfdf99SMichael Tritz } 388b1cfdf99SMichael Tritz 389b1cfdf99SMichael Tritz if (invalid) 39035e83f3eSSaqib Khan { 39135e83f3eSSaqib Khan return ItemUpdater::ActivationStatus::invalid; 39235e83f3eSSaqib Khan } 393b1cfdf99SMichael Tritz 394b1cfdf99SMichael Tritz return ItemUpdater::ActivationStatus::ready; 39535e83f3eSSaqib Khan } 39635e83f3eSSaqib Khan 397b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId) 3984c1aec09SSaqib Khan { 399b77551cdSAdriana Kobylak std::map<std::string, uint8_t> priorityMap; 400b77551cdSAdriana Kobylak 401b77551cdSAdriana Kobylak // Insert the requested version and priority, it may not exist yet. 402b77551cdSAdriana Kobylak priorityMap.insert(std::make_pair(versionId, value)); 403b77551cdSAdriana Kobylak 4044c1aec09SSaqib Khan for (const auto& intf : activations) 4054c1aec09SSaqib Khan { 4064c1aec09SSaqib Khan if (intf.second->redundancyPriority) 4074c1aec09SSaqib Khan { 408b77551cdSAdriana Kobylak priorityMap.insert(std::make_pair( 409b77551cdSAdriana Kobylak intf.first, 410b77551cdSAdriana Kobylak intf.second->redundancyPriority.get()->priority())); 411b77551cdSAdriana Kobylak } 412b77551cdSAdriana Kobylak } 413b77551cdSAdriana Kobylak 414b77551cdSAdriana Kobylak // Lambda function to compare 2 priority values, use <= to allow duplicates 415b77551cdSAdriana Kobylak typedef std::function<bool( 416b77551cdSAdriana Kobylak std::pair<std::string, uint8_t>, 417b77551cdSAdriana Kobylak std::pair<std::string, uint8_t>)> cmpPriority; 418b77551cdSAdriana Kobylak cmpPriority cmpPriorityFunc = []( 419b77551cdSAdriana Kobylak std::pair<std::string, uint8_t> priority1, 420b77551cdSAdriana Kobylak std::pair<std::string, uint8_t> priority2) 4214c1aec09SSaqib Khan { 422b77551cdSAdriana Kobylak return priority1.second <= priority2.second; 423b77551cdSAdriana Kobylak }; 424b77551cdSAdriana Kobylak 425b77551cdSAdriana Kobylak // Sort versions by ascending priority 426b77551cdSAdriana Kobylak std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet( 427b77551cdSAdriana Kobylak priorityMap.begin(), priorityMap.end(), cmpPriorityFunc); 428b77551cdSAdriana Kobylak 429b77551cdSAdriana Kobylak auto freePriorityValue = value; 430b77551cdSAdriana Kobylak for (auto& element : prioritySet) 431b77551cdSAdriana Kobylak { 432b77551cdSAdriana Kobylak if (element.first == versionId) 433b77551cdSAdriana Kobylak { 434b77551cdSAdriana Kobylak continue; 435b77551cdSAdriana Kobylak } 436b77551cdSAdriana Kobylak if (element.second == freePriorityValue) 437b77551cdSAdriana Kobylak { 438b77551cdSAdriana Kobylak ++freePriorityValue; 439b77551cdSAdriana Kobylak auto it = activations.find(element.first); 440b77551cdSAdriana Kobylak it->second->redundancyPriority.get()->sdbusPriority( 441b77551cdSAdriana Kobylak freePriorityValue); 4424c1aec09SSaqib Khan } 4434c1aec09SSaqib Khan } 444b77551cdSAdriana Kobylak 445b77551cdSAdriana Kobylak auto lowestVersion = prioritySet.begin()->first; 446b77551cdSAdriana Kobylak if (value == prioritySet.begin()->second) 447b77551cdSAdriana Kobylak { 448b77551cdSAdriana Kobylak lowestVersion = versionId; 4494c1aec09SSaqib Khan } 450b77551cdSAdriana Kobylak updateUbootEnvVars(lowestVersion); 4514c1aec09SSaqib Khan } 4524c1aec09SSaqib Khan 45337a59043SMichael Tritz void ItemUpdater::reset() 45437a59043SMichael Tritz { 45537a59043SMichael Tritz // Mark the read-write partition for recreation upon reboot. 45637a59043SMichael Tritz auto method = bus.new_method_call( 45737a59043SMichael Tritz SYSTEMD_BUSNAME, 45837a59043SMichael Tritz SYSTEMD_PATH, 45937a59043SMichael Tritz SYSTEMD_INTERFACE, 46037a59043SMichael Tritz "StartUnit"); 4610129d926SMichael Tritz method.append("obmc-flash-bmc-setenv@rwreset\\x3dtrue.service", "replace"); 46237a59043SMichael Tritz bus.call_noreply(method); 46337a59043SMichael Tritz 46437a59043SMichael Tritz log<level::INFO>("BMC factory reset will take effect upon reboot."); 46537a59043SMichael Tritz 46637a59043SMichael Tritz return; 46737a59043SMichael Tritz } 46837a59043SMichael Tritz 4693526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId) 4703526ef73SLeonel Gonzalez { 4713526ef73SLeonel Gonzalez auto serviceFile = "obmc-flash-bmc-ubiro-remove@" + versionId + 4723526ef73SLeonel Gonzalez ".service"; 4733526ef73SLeonel Gonzalez 4743526ef73SLeonel Gonzalez // Remove the read-only partitions. 4753526ef73SLeonel Gonzalez auto method = bus.new_method_call( 4763526ef73SLeonel Gonzalez SYSTEMD_BUSNAME, 4773526ef73SLeonel Gonzalez SYSTEMD_PATH, 4783526ef73SLeonel Gonzalez SYSTEMD_INTERFACE, 4793526ef73SLeonel Gonzalez "StartUnit"); 4803526ef73SLeonel Gonzalez method.append(serviceFile, "replace"); 4813526ef73SLeonel Gonzalez bus.call_noreply(method); 4823526ef73SLeonel Gonzalez } 4833526ef73SLeonel Gonzalez 4840129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value) 4850129d926SMichael Tritz { 4860129d926SMichael Tritz // enabling field mode is intended to be one way: false -> true 4870129d926SMichael Tritz if (value && !control::FieldMode::fieldModeEnabled()) 4880129d926SMichael Tritz { 4890129d926SMichael Tritz control::FieldMode::fieldModeEnabled(value); 4900129d926SMichael Tritz 4910129d926SMichael Tritz auto method = bus.new_method_call( 4920129d926SMichael Tritz SYSTEMD_BUSNAME, 4930129d926SMichael Tritz SYSTEMD_PATH, 4940129d926SMichael Tritz SYSTEMD_INTERFACE, 4950129d926SMichael Tritz "StartUnit"); 4960129d926SMichael Tritz method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service", 4970129d926SMichael Tritz "replace"); 4980129d926SMichael Tritz bus.call_noreply(method); 4990129d926SMichael Tritz 5000129d926SMichael Tritz method = bus.new_method_call( 5010129d926SMichael Tritz SYSTEMD_BUSNAME, 5020129d926SMichael Tritz SYSTEMD_PATH, 5030129d926SMichael Tritz SYSTEMD_INTERFACE, 5040129d926SMichael Tritz "StopUnit"); 5050129d926SMichael Tritz method.append("usr-local.mount", "replace"); 5060129d926SMichael Tritz bus.call_noreply(method); 5070129d926SMichael Tritz 5080129d926SMichael Tritz std::vector<std::string> usrLocal = {"usr-local.mount"}; 5090129d926SMichael Tritz 5100129d926SMichael Tritz method = bus.new_method_call( 5110129d926SMichael Tritz SYSTEMD_BUSNAME, 5120129d926SMichael Tritz SYSTEMD_PATH, 5130129d926SMichael Tritz SYSTEMD_INTERFACE, 5140129d926SMichael Tritz "MaskUnitFiles"); 5150129d926SMichael Tritz method.append(usrLocal, false, true); 5160129d926SMichael Tritz bus.call_noreply(method); 5170129d926SMichael Tritz } 5180129d926SMichael Tritz 5190129d926SMichael Tritz return control::FieldMode::fieldModeEnabled(); 5200129d926SMichael Tritz } 5210129d926SMichael Tritz 5220129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus() 5230129d926SMichael Tritz { 524ff0b421dSMichael Tritz std::ifstream input("/dev/mtd/u-boot-env"); 5250129d926SMichael Tritz std::string envVar; 5260129d926SMichael Tritz std::getline(input, envVar); 5270129d926SMichael Tritz 5280129d926SMichael Tritz if (envVar.find("fieldmode=true") != std::string::npos) 5290129d926SMichael Tritz { 5300129d926SMichael Tritz ItemUpdater::fieldModeEnabled(true); 5310129d926SMichael Tritz } 5320129d926SMichael Tritz } 5330129d926SMichael Tritz 534b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath() 535b60add1eSGunnar Mills { 536b60add1eSGunnar Mills auto depth = 0; 537b60add1eSGunnar Mills auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, 538b60add1eSGunnar Mills MAPPER_PATH, 539b60add1eSGunnar Mills MAPPER_INTERFACE, 540b60add1eSGunnar Mills "GetSubTreePaths"); 541b60add1eSGunnar Mills 542*1254c628SAdriana Kobylak mapperCall.append(INVENTORY_PATH); 543b60add1eSGunnar Mills mapperCall.append(depth); 544*1254c628SAdriana Kobylak std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE}; 545b60add1eSGunnar Mills mapperCall.append(filter); 546b60add1eSGunnar Mills 547b60add1eSGunnar Mills auto response = bus.call(mapperCall); 548b60add1eSGunnar Mills if (response.is_method_error()) 549b60add1eSGunnar Mills { 550b60add1eSGunnar Mills log<level::ERR>("Error in mapper GetSubTreePath"); 551b60add1eSGunnar Mills return; 552b60add1eSGunnar Mills } 553b60add1eSGunnar Mills 554b60add1eSGunnar Mills using ObjectPaths = std::vector<std::string>; 555b60add1eSGunnar Mills ObjectPaths result; 556b60add1eSGunnar Mills response.read(result); 557b60add1eSGunnar Mills 558*1254c628SAdriana Kobylak if (!result.empty()) 559b60add1eSGunnar Mills { 560*1254c628SAdriana Kobylak bmcInventoryPath = result.front(); 561b60add1eSGunnar Mills } 562b60add1eSGunnar Mills 563b60add1eSGunnar Mills return; 564b60add1eSGunnar Mills } 565b60add1eSGunnar Mills 566f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path) 567ded875dcSGunnar Mills { 568ded875dcSGunnar Mills assocs.emplace_back(std::make_tuple(ACTIVE_FWD_ASSOCIATION, 569ded875dcSGunnar Mills ACTIVE_REV_ASSOCIATION, 570ded875dcSGunnar Mills path)); 571ded875dcSGunnar Mills associations(assocs); 572ded875dcSGunnar Mills } 573ded875dcSGunnar Mills 57488e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path) 57588e8a325SGunnar Mills { 57688e8a325SGunnar Mills assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION, 57788e8a325SGunnar Mills FUNCTIONAL_REV_ASSOCIATION, 57888e8a325SGunnar Mills path)); 57988e8a325SGunnar Mills associations(assocs); 58088e8a325SGunnar Mills } 58188e8a325SGunnar Mills 582f10b2326SGunnar Mills void ItemUpdater::removeActiveAssociation(const std::string& path) 583ded875dcSGunnar Mills { 584ded875dcSGunnar Mills for (auto iter = assocs.begin(); iter != assocs.end();) 585ded875dcSGunnar Mills { 58688e8a325SGunnar Mills // Since there could be multiple associations to the same path, 58788e8a325SGunnar Mills // only remove ones that have an active forward association. 58888e8a325SGunnar Mills if ((std::get<0>(*iter)).compare(ACTIVE_FWD_ASSOCIATION) == 0 && 58988e8a325SGunnar Mills (std::get<2>(*iter)).compare(path) == 0) 590ded875dcSGunnar Mills { 591ded875dcSGunnar Mills iter = assocs.erase(iter); 592ded875dcSGunnar Mills associations(assocs); 593ded875dcSGunnar Mills } 594ded875dcSGunnar Mills else 595ded875dcSGunnar Mills { 596ded875dcSGunnar Mills ++iter; 597ded875dcSGunnar Mills } 598ded875dcSGunnar Mills } 599ded875dcSGunnar Mills } 600ded875dcSGunnar Mills 601b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value) 602b9da6634SSaqib Khan { 603b9da6634SSaqib Khan for (const auto& intf : activations) 604b9da6634SSaqib Khan { 605b9da6634SSaqib Khan if (intf.second->redundancyPriority) 606b9da6634SSaqib Khan { 607b9da6634SSaqib Khan if (intf.second->redundancyPriority.get()->priority() < value) 608b9da6634SSaqib Khan { 609b9da6634SSaqib Khan return false; 610b9da6634SSaqib Khan } 611b9da6634SSaqib Khan } 612b9da6634SSaqib Khan } 613b9da6634SSaqib Khan return true; 614b9da6634SSaqib Khan } 615b9da6634SSaqib Khan 616b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId) 617b77551cdSAdriana Kobylak { 618b77551cdSAdriana Kobylak auto method = bus.new_method_call( 619b77551cdSAdriana Kobylak SYSTEMD_BUSNAME, 620b77551cdSAdriana Kobylak SYSTEMD_PATH, 621b77551cdSAdriana Kobylak SYSTEMD_INTERFACE, 622b77551cdSAdriana Kobylak "StartUnit"); 623b77551cdSAdriana Kobylak auto updateEnvVarsFile = "obmc-flash-bmc-updateubootvars@" + versionId + 624b77551cdSAdriana Kobylak ".service"; 625b77551cdSAdriana Kobylak method.append(updateEnvVarsFile, "replace"); 626b77551cdSAdriana Kobylak auto result = bus.call(method); 627b77551cdSAdriana Kobylak 628b77551cdSAdriana Kobylak //Check that the bus call didn't result in an error 629b77551cdSAdriana Kobylak if (result.is_method_error()) 630b77551cdSAdriana Kobylak { 631b77551cdSAdriana Kobylak log<level::ERR>("Failed to update u-boot env variables", 632b77551cdSAdriana Kobylak entry("VERSIONID=%s", versionId)); 633b77551cdSAdriana Kobylak } 634b77551cdSAdriana Kobylak } 635b77551cdSAdriana Kobylak 63649446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars() 63749446ae9SSaqib Khan { 63849446ae9SSaqib Khan decltype(activations.begin()->second->redundancyPriority.get()->priority()) 63949446ae9SSaqib Khan lowestPriority = std::numeric_limits<uint8_t>::max(); 64049446ae9SSaqib Khan decltype(activations.begin()->second->versionId) lowestPriorityVersion; 64149446ae9SSaqib Khan for (const auto& intf : activations) 64249446ae9SSaqib Khan { 64349446ae9SSaqib Khan if (!intf.second->redundancyPriority.get()) 64449446ae9SSaqib Khan { 64549446ae9SSaqib Khan // Skip this version if the redundancyPriority is not initialized. 64649446ae9SSaqib Khan continue; 64749446ae9SSaqib Khan } 64849446ae9SSaqib Khan 64949446ae9SSaqib Khan if (intf.second->redundancyPriority.get()->priority() 65049446ae9SSaqib Khan <= lowestPriority) 65149446ae9SSaqib Khan { 65249446ae9SSaqib Khan lowestPriority = intf.second->redundancyPriority.get()->priority(); 65349446ae9SSaqib Khan lowestPriorityVersion = intf.second->versionId; 65449446ae9SSaqib Khan } 65549446ae9SSaqib Khan } 65649446ae9SSaqib Khan 657f0382c35SSaqib Khan // Update the U-boot environment variable to point to the lowest priority 658b77551cdSAdriana Kobylak updateUbootEnvVars(lowestPriorityVersion); 65949446ae9SSaqib Khan } 66049446ae9SSaqib Khan 661ec1b41c4SGunnar Mills } // namespace updater 662ec1b41c4SGunnar Mills } // namespace software 663ec1b41c4SGunnar Mills } // namespace phosphor 664