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"
151be8d500SLei 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;
137*269bff30SLei YU 
138*269bff30SLei YU     // Check MEDIA_DIR and create if it does not exist
139*269bff30SLei YU     try
140*269bff30SLei YU     {
141*269bff30SLei YU         if (!fs::is_directory(MEDIA_DIR))
142*269bff30SLei YU         {
143*269bff30SLei YU             fs::create_directory(MEDIA_DIR);
144*269bff30SLei YU         }
145*269bff30SLei YU     }
146*269bff30SLei YU     catch (const fs::filesystem_error& e)
147*269bff30SLei YU     {
148*269bff30SLei YU         log<level::ERR>("Failed to prepare dir", entry("ERR=%s", e.what()));
149*269bff30SLei YU         return;
150*269bff30SLei YU     }
151*269bff30SLei YU 
15288e8a325SGunnar Mills     // Read os-release from /etc/ to get the functional BMC version
15388e8a325SGunnar Mills     auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE);
15488e8a325SGunnar Mills 
1551eef62deSSaqib Khan     // Read os-release from folders under /media/ to get
1561eef62deSSaqib Khan     // BMC Software Versions.
1571eef62deSSaqib Khan     for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
1581eef62deSSaqib Khan     {
1591eef62deSSaqib Khan         auto activationState = server::Activation::Activations::Active;
1606fab70daSSaqib Khan         static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX);
1611eef62deSSaqib Khan 
1621eef62deSSaqib Khan         // Check if the BMC_RO_PREFIXis the prefix of the iter.path
1632285fe0fSAdriana Kobylak         if (0 ==
1642285fe0fSAdriana Kobylak             iter.path().native().compare(0, BMC_RO_PREFIX_LEN, BMC_ROFS_PREFIX))
1651eef62deSSaqib Khan         {
166021c365bSSaqib Khan             // The versionId is extracted from the path
167021c365bSSaqib Khan             // for example /media/ro-2a1022fe.
168021c365bSSaqib Khan             auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
1691eef62deSSaqib Khan             auto osRelease = iter.path() / OS_RELEASE_FILE;
1701eef62deSSaqib Khan             if (!fs::is_regular_file(osRelease))
1711eef62deSSaqib Khan             {
1722285fe0fSAdriana Kobylak                 log<level::ERR>(
1732285fe0fSAdriana Kobylak                     "Failed to read osRelease",
174596466b8SAdriana Kobylak                     entry("FILENAME=%s", osRelease.string().c_str()));
175021c365bSSaqib Khan                 ItemUpdater::erase(id);
176021c365bSSaqib Khan                 continue;
1771eef62deSSaqib Khan             }
17888e8a325SGunnar Mills             auto version = VersionClass::getBMCVersion(osRelease);
1791eef62deSSaqib Khan             if (version.empty())
1801eef62deSSaqib Khan             {
1812285fe0fSAdriana Kobylak                 log<level::ERR>(
1822285fe0fSAdriana Kobylak                     "Failed to read version from osRelease",
183596466b8SAdriana Kobylak                     entry("FILENAME=%s", osRelease.string().c_str()));
1841eef62deSSaqib Khan                 activationState = server::Activation::Activations::Invalid;
1851eef62deSSaqib Khan             }
186021c365bSSaqib Khan 
1871eef62deSSaqib Khan             auto purpose = server::Version::VersionPurpose::BMC;
1881eef62deSSaqib Khan             auto path = fs::path(SOFTWARE_OBJPATH) / id;
1891eef62deSSaqib Khan 
190*269bff30SLei YU             // Create functional association if this is the functional
191*269bff30SLei YU             // version
19288e8a325SGunnar Mills             if (version.compare(functionalVersion) == 0)
19388e8a325SGunnar Mills             {
19488e8a325SGunnar Mills                 createFunctionalAssociation(path);
19588e8a325SGunnar Mills             }
19688e8a325SGunnar Mills 
19743b25cdeSGunnar Mills             AssociationList associations = {};
19843b25cdeSGunnar Mills 
19943b25cdeSGunnar Mills             if (activationState == server::Activation::Activations::Active)
20043b25cdeSGunnar Mills             {
20143b25cdeSGunnar Mills                 // Create an association to the BMC inventory item
20243b25cdeSGunnar Mills                 associations.emplace_back(std::make_tuple(
2032285fe0fSAdriana Kobylak                     ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
20443b25cdeSGunnar Mills                     bmcInventoryPath));
20543b25cdeSGunnar Mills 
20643b25cdeSGunnar Mills                 // Create an active association since this image is active
20743b25cdeSGunnar Mills                 createActiveAssociation(path);
20843b25cdeSGunnar Mills             }
20943b25cdeSGunnar Mills 
210ee590c74SAdriana Kobylak             // Create Version instance for this version.
211ee590c74SAdriana Kobylak             auto versionPtr = std::make_unique<VersionClass>(
2122285fe0fSAdriana Kobylak                 bus, path, version, purpose, "",
2132285fe0fSAdriana Kobylak                 std::bind(&ItemUpdater::erase, this, std::placeholders::_1));
214ee590c74SAdriana Kobylak             auto isVersionFunctional = versionPtr->isFunctional();
215ee13e831SSaqib Khan             if (!isVersionFunctional)
216ee13e831SSaqib Khan             {
217ee13e831SSaqib Khan                 versionPtr->deleteObject =
218ee13e831SSaqib Khan                     std::make_unique<phosphor::software::manager::Delete>(
219ee13e831SSaqib Khan                         bus, path, *versionPtr);
220ee13e831SSaqib Khan             }
2212285fe0fSAdriana Kobylak             versions.insert(std::make_pair(id, std::move(versionPtr)));
222ee590c74SAdriana Kobylak 
2231eef62deSSaqib Khan             // Create Activation instance for this version.
224ee13e831SSaqib Khan             activations.insert(std::make_pair(
2252285fe0fSAdriana Kobylak                 id, std::make_unique<Activation>(
2262285fe0fSAdriana Kobylak                         bus, path, *this, id, activationState, associations)));
2271eef62deSSaqib Khan 
228*269bff30SLei YU             // If Active, create RedundancyPriority instance for this
229*269bff30SLei YU             // version.
2301eef62deSSaqib Khan             if (activationState == server::Activation::Activations::Active)
2311eef62deSSaqib Khan             {
2321eef62deSSaqib Khan                 uint8_t priority = std::numeric_limits<uint8_t>::max();
2331eef62deSSaqib Khan                 if (!restoreFromFile(id, priority))
2341eef62deSSaqib Khan                 {
235ee590c74SAdriana Kobylak                     if (isVersionFunctional)
236ee590c74SAdriana Kobylak                     {
237ee590c74SAdriana Kobylak                         priority = 0;
238ee590c74SAdriana Kobylak                     }
239ee590c74SAdriana Kobylak                     else
240ee590c74SAdriana Kobylak                     {
2411eef62deSSaqib Khan                         log<level::ERR>("Unable to restore priority from file.",
242596466b8SAdriana Kobylak                                         entry("VERSIONID=%s", id.c_str()));
2431eef62deSSaqib Khan                     }
244ee590c74SAdriana Kobylak                 }
2451eef62deSSaqib Khan                 activations.find(id)->second->redundancyPriority =
2461eef62deSSaqib Khan                     std::make_unique<RedundancyPriority>(
2472285fe0fSAdriana Kobylak                         bus, path, *(activations.find(id)->second), priority,
248b77551cdSAdriana Kobylak                         false);
2491eef62deSSaqib Khan             }
2501eef62deSSaqib Khan         }
2511eef62deSSaqib Khan     }
252dcbfa04aSSaqib Khan 
253dcbfa04aSSaqib Khan     // If there is no ubi volume for bmc version then read the /etc/os-release
254dcbfa04aSSaqib Khan     // and create rofs-<versionId> under /media
255dcbfa04aSSaqib Khan     if (activations.size() == 0)
256dcbfa04aSSaqib Khan     {
257d16bcbd5SGunnar Mills         auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE);
258dcbfa04aSSaqib Khan         auto id = phosphor::software::manager::Version::getId(version);
259dcbfa04aSSaqib Khan         auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/";
260dcbfa04aSSaqib Khan         try
261dcbfa04aSSaqib Khan         {
262dcbfa04aSSaqib Khan             if (!fs::is_directory(versionFileDir))
263dcbfa04aSSaqib Khan             {
264dcbfa04aSSaqib Khan                 fs::create_directories(versionFileDir);
265dcbfa04aSSaqib Khan             }
266dcbfa04aSSaqib Khan             auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE;
267dcbfa04aSSaqib Khan             fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath);
268dcbfa04aSSaqib Khan             ItemUpdater::processBMCImage();
269dcbfa04aSSaqib Khan         }
270dcbfa04aSSaqib Khan         catch (const std::exception& e)
271dcbfa04aSSaqib Khan         {
272dcbfa04aSSaqib Khan             log<level::ERR>(e.what());
273dcbfa04aSSaqib Khan         }
274dcbfa04aSSaqib Khan     }
275eaa1ee05SEddie James 
276eaa1ee05SEddie James     mirrorUbootToAlt();
277ba239881SSaqib Khan     return;
278ba239881SSaqib Khan }
279ba239881SSaqib Khan 
2803526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId)
2813526ef73SLeonel Gonzalez {
2826d873715SEddie James     // Find entry in versions map
2836d873715SEddie James     auto it = versions.find(entryId);
2846d873715SEddie James     if (it != versions.end())
2856d873715SEddie James     {
2866d873715SEddie James         if (it->second->isFunctional())
2876d873715SEddie James         {
2882285fe0fSAdriana Kobylak             log<level::ERR>("Error: Version is currently running on the BMC. "
2892285fe0fSAdriana Kobylak                             "Unable to remove.",
2902285fe0fSAdriana Kobylak                             entry("VERSIONID=%s", entryId.c_str()));
2916d873715SEddie James             return;
2926d873715SEddie James         }
2936d873715SEddie James 
2946d873715SEddie James         // Delete ReadOnly partitions if it's not active
2953526ef73SLeonel Gonzalez         removeReadOnlyPartition(entryId);
2961eef62deSSaqib Khan         removeFile(entryId);
297ee13e831SSaqib Khan 
298ee13e831SSaqib Khan         // Removing entry in versions map
299ee13e831SSaqib Khan         this->versions.erase(entryId);
3006d873715SEddie James     }
3016d873715SEddie James     else
3026d873715SEddie James     {
3036d873715SEddie James         // Delete ReadOnly partitions even if we can't find the version
3046d873715SEddie James         removeReadOnlyPartition(entryId);
3056d873715SEddie James         removeFile(entryId);
3066d873715SEddie James 
3072285fe0fSAdriana Kobylak         log<level::ERR>("Error: Failed to find version in item updater "
3082285fe0fSAdriana Kobylak                         "versions map. Unable to remove.",
3092285fe0fSAdriana Kobylak                         entry("VERSIONID=%s", entryId.c_str()));
3106d873715SEddie James     }
3111eef62deSSaqib Khan 
31256aaf454SLei YU     helper.clearEntry(entryId);
3133526ef73SLeonel Gonzalez 
3143526ef73SLeonel Gonzalez     // Removing entry in activations map
3153526ef73SLeonel Gonzalez     auto ita = activations.find(entryId);
3163526ef73SLeonel Gonzalez     if (ita == activations.end())
3173526ef73SLeonel Gonzalez     {
3182285fe0fSAdriana Kobylak         log<level::ERR>("Error: Failed to find version in item updater "
3192285fe0fSAdriana Kobylak                         "activations map. Unable to remove.",
3202285fe0fSAdriana Kobylak                         entry("VERSIONID=%s", entryId.c_str()));
3213526ef73SLeonel Gonzalez     }
322ee13e831SSaqib Khan     else
323ee13e831SSaqib Khan     {
3243526ef73SLeonel Gonzalez         this->activations.erase(entryId);
325ee13e831SSaqib Khan     }
32649446ae9SSaqib Khan     ItemUpdater::resetUbootEnvVars();
327ee13e831SSaqib Khan     return;
3283526ef73SLeonel Gonzalez }
3293526ef73SLeonel Gonzalez 
330bc1bf3afSMichael Tritz void ItemUpdater::deleteAll()
331bc1bf3afSMichael Tritz {
33283cd21fbSAdriana Kobylak     std::vector<std::string> deletableVersions;
33383cd21fbSAdriana Kobylak 
334bc1bf3afSMichael Tritz     for (const auto& versionIt : versions)
335bc1bf3afSMichael Tritz     {
336bc1bf3afSMichael Tritz         if (!versionIt.second->isFunctional())
337bc1bf3afSMichael Tritz         {
33883cd21fbSAdriana Kobylak             deletableVersions.push_back(versionIt.first);
339bc1bf3afSMichael Tritz         }
340bc1bf3afSMichael Tritz     }
341bc1bf3afSMichael Tritz 
34283cd21fbSAdriana Kobylak     for (const auto& deletableIt : deletableVersions)
34383cd21fbSAdriana Kobylak     {
34483cd21fbSAdriana Kobylak         ItemUpdater::erase(deletableIt);
34583cd21fbSAdriana Kobylak     }
34683cd21fbSAdriana Kobylak 
34756aaf454SLei YU     helper.cleanup();
348bc1bf3afSMichael Tritz }
349bc1bf3afSMichael Tritz 
3502285fe0fSAdriana Kobylak ItemUpdater::ActivationStatus
3512285fe0fSAdriana Kobylak     ItemUpdater::validateSquashFSImage(const std::string& filePath)
35235e83f3eSSaqib Khan {
353b1cfdf99SMichael Tritz     bool invalid = false;
35435e83f3eSSaqib Khan 
355b1cfdf99SMichael Tritz     for (auto& bmcImage : bmcImages)
356b1cfdf99SMichael Tritz     {
35719177d3eSSaqib Khan         fs::path file(filePath);
35835e83f3eSSaqib Khan         file /= bmcImage;
35935e83f3eSSaqib Khan         std::ifstream efile(file.c_str());
360b1cfdf99SMichael Tritz         if (efile.good() != 1)
36135e83f3eSSaqib Khan         {
362b1cfdf99SMichael Tritz             log<level::ERR>("Failed to find the BMC image.",
363b1cfdf99SMichael Tritz                             entry("IMAGE=%s", bmcImage.c_str()));
364b1cfdf99SMichael Tritz             invalid = true;
36535e83f3eSSaqib Khan         }
366b1cfdf99SMichael Tritz     }
367b1cfdf99SMichael Tritz 
368b1cfdf99SMichael Tritz     if (invalid)
36935e83f3eSSaqib Khan     {
37035e83f3eSSaqib Khan         return ItemUpdater::ActivationStatus::invalid;
37135e83f3eSSaqib Khan     }
372b1cfdf99SMichael Tritz 
373b1cfdf99SMichael Tritz     return ItemUpdater::ActivationStatus::ready;
37435e83f3eSSaqib Khan }
37535e83f3eSSaqib Khan 
376bbcb7be1SAdriana Kobylak void ItemUpdater::savePriority(const std::string& versionId, uint8_t value)
377bbcb7be1SAdriana Kobylak {
378bbcb7be1SAdriana Kobylak     storeToFile(versionId, value);
379bbcb7be1SAdriana Kobylak     helper.setEntry(versionId, value);
380bbcb7be1SAdriana Kobylak }
381bbcb7be1SAdriana Kobylak 
382b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
3834c1aec09SSaqib Khan {
384b77551cdSAdriana Kobylak     std::map<std::string, uint8_t> priorityMap;
385b77551cdSAdriana Kobylak 
386b77551cdSAdriana Kobylak     // Insert the requested version and priority, it may not exist yet.
387b77551cdSAdriana Kobylak     priorityMap.insert(std::make_pair(versionId, value));
388b77551cdSAdriana Kobylak 
3894c1aec09SSaqib Khan     for (const auto& intf : activations)
3904c1aec09SSaqib Khan     {
3914c1aec09SSaqib Khan         if (intf.second->redundancyPriority)
3924c1aec09SSaqib Khan         {
393b77551cdSAdriana Kobylak             priorityMap.insert(std::make_pair(
3942285fe0fSAdriana Kobylak                 intf.first, intf.second->redundancyPriority.get()->priority()));
395b77551cdSAdriana Kobylak         }
396b77551cdSAdriana Kobylak     }
397b77551cdSAdriana Kobylak 
398b77551cdSAdriana Kobylak     // Lambda function to compare 2 priority values, use <= to allow duplicates
3992285fe0fSAdriana Kobylak     typedef std::function<bool(std::pair<std::string, uint8_t>,
4002285fe0fSAdriana Kobylak                                std::pair<std::string, uint8_t>)>
4012285fe0fSAdriana Kobylak         cmpPriority;
4022285fe0fSAdriana Kobylak     cmpPriority cmpPriorityFunc =
4032285fe0fSAdriana Kobylak         [](std::pair<std::string, uint8_t> priority1,
4042285fe0fSAdriana Kobylak            std::pair<std::string, uint8_t> priority2) {
405b77551cdSAdriana Kobylak             return priority1.second <= priority2.second;
406b77551cdSAdriana Kobylak         };
407b77551cdSAdriana Kobylak 
408b77551cdSAdriana Kobylak     // Sort versions by ascending priority
409b77551cdSAdriana Kobylak     std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet(
410b77551cdSAdriana Kobylak         priorityMap.begin(), priorityMap.end(), cmpPriorityFunc);
411b77551cdSAdriana Kobylak 
412b77551cdSAdriana Kobylak     auto freePriorityValue = value;
413b77551cdSAdriana Kobylak     for (auto& element : prioritySet)
414b77551cdSAdriana Kobylak     {
415b77551cdSAdriana Kobylak         if (element.first == versionId)
416b77551cdSAdriana Kobylak         {
417b77551cdSAdriana Kobylak             continue;
418b77551cdSAdriana Kobylak         }
419b77551cdSAdriana Kobylak         if (element.second == freePriorityValue)
420b77551cdSAdriana Kobylak         {
421b77551cdSAdriana Kobylak             ++freePriorityValue;
422b77551cdSAdriana Kobylak             auto it = activations.find(element.first);
423b77551cdSAdriana Kobylak             it->second->redundancyPriority.get()->sdbusPriority(
424b77551cdSAdriana Kobylak                 freePriorityValue);
4254c1aec09SSaqib Khan         }
4264c1aec09SSaqib Khan     }
427b77551cdSAdriana Kobylak 
428b77551cdSAdriana Kobylak     auto lowestVersion = prioritySet.begin()->first;
429b77551cdSAdriana Kobylak     if (value == prioritySet.begin()->second)
430b77551cdSAdriana Kobylak     {
431b77551cdSAdriana Kobylak         lowestVersion = versionId;
4324c1aec09SSaqib Khan     }
433b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestVersion);
4344c1aec09SSaqib Khan }
4354c1aec09SSaqib Khan 
43637a59043SMichael Tritz void ItemUpdater::reset()
43737a59043SMichael Tritz {
43856aaf454SLei YU     helper.factoryReset();
43937a59043SMichael Tritz 
44037a59043SMichael Tritz     log<level::INFO>("BMC factory reset will take effect upon reboot.");
44137a59043SMichael Tritz }
44237a59043SMichael Tritz 
4433526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId)
4443526ef73SLeonel Gonzalez {
44556aaf454SLei YU     helper.removeVersion(versionId);
4463526ef73SLeonel Gonzalez }
4473526ef73SLeonel Gonzalez 
4480129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value)
4490129d926SMichael Tritz {
4500129d926SMichael Tritz     // enabling field mode is intended to be one way: false -> true
4510129d926SMichael Tritz     if (value && !control::FieldMode::fieldModeEnabled())
4520129d926SMichael Tritz     {
4530129d926SMichael Tritz         control::FieldMode::fieldModeEnabled(value);
4540129d926SMichael Tritz 
45556aaf454SLei YU         helper.enableFieldMode();
4560129d926SMichael Tritz     }
4570129d926SMichael Tritz 
4580129d926SMichael Tritz     return control::FieldMode::fieldModeEnabled();
4590129d926SMichael Tritz }
4600129d926SMichael Tritz 
4610129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus()
4620129d926SMichael Tritz {
463ff0b421dSMichael Tritz     std::ifstream input("/dev/mtd/u-boot-env");
4640129d926SMichael Tritz     std::string envVar;
4650129d926SMichael Tritz     std::getline(input, envVar);
4660129d926SMichael Tritz 
4670129d926SMichael Tritz     if (envVar.find("fieldmode=true") != std::string::npos)
4680129d926SMichael Tritz     {
4690129d926SMichael Tritz         ItemUpdater::fieldModeEnabled(true);
4700129d926SMichael Tritz     }
4710129d926SMichael Tritz }
4720129d926SMichael Tritz 
473b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath()
474b60add1eSGunnar Mills {
475b60add1eSGunnar Mills     auto depth = 0;
4762285fe0fSAdriana Kobylak     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
4772285fe0fSAdriana Kobylak                                           MAPPER_INTERFACE, "GetSubTreePaths");
478b60add1eSGunnar Mills 
4791254c628SAdriana Kobylak     mapperCall.append(INVENTORY_PATH);
480b60add1eSGunnar Mills     mapperCall.append(depth);
4811254c628SAdriana Kobylak     std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE};
482b60add1eSGunnar Mills     mapperCall.append(filter);
483b60add1eSGunnar Mills 
48487c78173SEd Tanous     try
485b60add1eSGunnar Mills     {
48687c78173SEd Tanous         auto response = bus.call(mapperCall);
487b60add1eSGunnar Mills 
488b60add1eSGunnar Mills         using ObjectPaths = std::vector<std::string>;
489b60add1eSGunnar Mills         ObjectPaths result;
490b60add1eSGunnar Mills         response.read(result);
491b60add1eSGunnar Mills 
4921254c628SAdriana Kobylak         if (!result.empty())
493b60add1eSGunnar Mills         {
4941254c628SAdriana Kobylak             bmcInventoryPath = result.front();
495b60add1eSGunnar Mills         }
49687c78173SEd Tanous     }
49787c78173SEd Tanous     catch (const sdbusplus::exception::SdBusError& e)
49887c78173SEd Tanous     {
49987c78173SEd Tanous         log<level::ERR>("Error in mapper GetSubTreePath");
50087c78173SEd Tanous         return;
50187c78173SEd Tanous     }
502b60add1eSGunnar Mills 
503b60add1eSGunnar Mills     return;
504b60add1eSGunnar Mills }
505b60add1eSGunnar Mills 
506f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path)
507ded875dcSGunnar Mills {
5082285fe0fSAdriana Kobylak     assocs.emplace_back(
5092285fe0fSAdriana Kobylak         std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
510ded875dcSGunnar Mills     associations(assocs);
511ded875dcSGunnar Mills }
512ded875dcSGunnar Mills 
51388e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path)
51488e8a325SGunnar Mills {
51588e8a325SGunnar Mills     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
5162285fe0fSAdriana Kobylak                                         FUNCTIONAL_REV_ASSOCIATION, path));
51788e8a325SGunnar Mills     associations(assocs);
51888e8a325SGunnar Mills }
51988e8a325SGunnar Mills 
520f10b2326SGunnar Mills void ItemUpdater::removeActiveAssociation(const std::string& path)
521ded875dcSGunnar Mills {
522ded875dcSGunnar Mills     for (auto iter = assocs.begin(); iter != assocs.end();)
523ded875dcSGunnar Mills     {
52488e8a325SGunnar Mills         // Since there could be multiple associations to the same path,
52588e8a325SGunnar Mills         // only remove ones that have an active forward association.
52688e8a325SGunnar Mills         if ((std::get<0>(*iter)).compare(ACTIVE_FWD_ASSOCIATION) == 0 &&
52788e8a325SGunnar Mills             (std::get<2>(*iter)).compare(path) == 0)
528ded875dcSGunnar Mills         {
529ded875dcSGunnar Mills             iter = assocs.erase(iter);
530ded875dcSGunnar Mills             associations(assocs);
531ded875dcSGunnar Mills         }
532ded875dcSGunnar Mills         else
533ded875dcSGunnar Mills         {
534ded875dcSGunnar Mills             ++iter;
535ded875dcSGunnar Mills         }
536ded875dcSGunnar Mills     }
537ded875dcSGunnar Mills }
538ded875dcSGunnar Mills 
539b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value)
540b9da6634SSaqib Khan {
541b9da6634SSaqib Khan     for (const auto& intf : activations)
542b9da6634SSaqib Khan     {
543b9da6634SSaqib Khan         if (intf.second->redundancyPriority)
544b9da6634SSaqib Khan         {
545b9da6634SSaqib Khan             if (intf.second->redundancyPriority.get()->priority() < value)
546b9da6634SSaqib Khan             {
547b9da6634SSaqib Khan                 return false;
548b9da6634SSaqib Khan             }
549b9da6634SSaqib Khan         }
550b9da6634SSaqib Khan     }
551b9da6634SSaqib Khan     return true;
552b9da6634SSaqib Khan }
553b9da6634SSaqib Khan 
554b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId)
555b77551cdSAdriana Kobylak {
55656aaf454SLei YU     helper.updateUbootVersionId(versionId);
557b77551cdSAdriana Kobylak }
558b77551cdSAdriana Kobylak 
55949446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars()
56049446ae9SSaqib Khan {
56149446ae9SSaqib Khan     decltype(activations.begin()->second->redundancyPriority.get()->priority())
56249446ae9SSaqib Khan         lowestPriority = std::numeric_limits<uint8_t>::max();
56349446ae9SSaqib Khan     decltype(activations.begin()->second->versionId) lowestPriorityVersion;
56449446ae9SSaqib Khan     for (const auto& intf : activations)
56549446ae9SSaqib Khan     {
56649446ae9SSaqib Khan         if (!intf.second->redundancyPriority.get())
56749446ae9SSaqib Khan         {
56849446ae9SSaqib Khan             // Skip this version if the redundancyPriority is not initialized.
56949446ae9SSaqib Khan             continue;
57049446ae9SSaqib Khan         }
57149446ae9SSaqib Khan 
5722285fe0fSAdriana Kobylak         if (intf.second->redundancyPriority.get()->priority() <= lowestPriority)
57349446ae9SSaqib Khan         {
57449446ae9SSaqib Khan             lowestPriority = intf.second->redundancyPriority.get()->priority();
57549446ae9SSaqib Khan             lowestPriorityVersion = intf.second->versionId;
57649446ae9SSaqib Khan         }
57749446ae9SSaqib Khan     }
57849446ae9SSaqib Khan 
579f0382c35SSaqib Khan     // Update the U-boot environment variable to point to the lowest priority
580b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestPriorityVersion);
58149446ae9SSaqib Khan }
58249446ae9SSaqib Khan 
583204e1e74SAdriana Kobylak void ItemUpdater::freeSpace()
584204e1e74SAdriana Kobylak {
585204e1e74SAdriana Kobylak     //  Versions with the highest priority in front
586204e1e74SAdriana Kobylak     std::priority_queue<std::pair<int, std::string>,
587204e1e74SAdriana Kobylak                         std::vector<std::pair<int, std::string>>,
5882285fe0fSAdriana Kobylak                         std::less<std::pair<int, std::string>>>
5892285fe0fSAdriana Kobylak         versionsPQ;
590204e1e74SAdriana Kobylak 
591204e1e74SAdriana Kobylak     std::size_t count = 0;
592204e1e74SAdriana Kobylak     for (const auto& iter : activations)
593204e1e74SAdriana Kobylak     {
594204e1e74SAdriana Kobylak         if ((iter.second.get()->activation() ==
595204e1e74SAdriana Kobylak              server::Activation::Activations::Active) ||
596204e1e74SAdriana Kobylak             (iter.second.get()->activation() ==
597204e1e74SAdriana Kobylak              server::Activation::Activations::Failed))
598204e1e74SAdriana Kobylak         {
599204e1e74SAdriana Kobylak             count++;
600204e1e74SAdriana Kobylak             // Don't put the functional version on the queue since we can't
601204e1e74SAdriana Kobylak             // remove the "running" BMC version.
602204e1e74SAdriana Kobylak             if (versions.find(iter.second->versionId)->second->isFunctional())
603204e1e74SAdriana Kobylak             {
604204e1e74SAdriana Kobylak                 continue;
605204e1e74SAdriana Kobylak             }
606204e1e74SAdriana Kobylak             versionsPQ.push(std::make_pair(
607204e1e74SAdriana Kobylak                 iter.second->redundancyPriority.get()->priority(),
608204e1e74SAdriana Kobylak                 iter.second->versionId));
609204e1e74SAdriana Kobylak         }
610204e1e74SAdriana Kobylak     }
611204e1e74SAdriana Kobylak 
612204e1e74SAdriana Kobylak     // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1,
613204e1e74SAdriana Kobylak     // remove the highest priority one(s).
614204e1e74SAdriana Kobylak     while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty()))
615204e1e74SAdriana Kobylak     {
616204e1e74SAdriana Kobylak         erase(versionsPQ.top().second);
617204e1e74SAdriana Kobylak         versionsPQ.pop();
618204e1e74SAdriana Kobylak         count--;
619204e1e74SAdriana Kobylak     }
620204e1e74SAdriana Kobylak }
621204e1e74SAdriana Kobylak 
622eaa1ee05SEddie James void ItemUpdater::mirrorUbootToAlt()
623eaa1ee05SEddie James {
62456aaf454SLei YU     helper.mirrorAlt();
625eaa1ee05SEddie James }
626eaa1ee05SEddie James 
627ec1b41c4SGunnar Mills } // namespace updater
628ec1b41c4SGunnar Mills } // namespace software
629ec1b41c4SGunnar Mills } // namespace phosphor
630