1b0ce996aSGunnar Mills #include "config.h"
2b0ce996aSGunnar Mills 
3b0ce996aSGunnar Mills #include "item_updater.hpp"
4b0ce996aSGunnar Mills 
5b0ce996aSGunnar Mills #include "images.hpp"
6b0ce996aSGunnar Mills #include "serialize.hpp"
7b0ce996aSGunnar Mills #include "version.hpp"
8b0ce996aSGunnar Mills #include "xyz/openbmc_project/Software/Version/server.hpp"
9b0ce996aSGunnar Mills 
10b0ce996aSGunnar Mills #include <elog-errors.hpp>
11b0ce996aSGunnar Mills #include <experimental/filesystem>
1235e83f3eSSaqib Khan #include <fstream>
13b0ce996aSGunnar Mills #include <phosphor-logging/elog.hpp>
14b0ce996aSGunnar Mills #include <phosphor-logging/log.hpp>
15204e1e74SAdriana Kobylak #include <queue>
16b77551cdSAdriana Kobylak #include <set>
17ec1b41c4SGunnar Mills #include <string>
18dcbfa04aSSaqib Khan #include <xyz/openbmc_project/Software/Version/error.hpp>
19ec1b41c4SGunnar Mills 
20ec1b41c4SGunnar Mills namespace phosphor
21ec1b41c4SGunnar Mills {
22ec1b41c4SGunnar Mills namespace software
23ec1b41c4SGunnar Mills {
24ec1b41c4SGunnar Mills namespace updater
25ec1b41c4SGunnar Mills {
26ec1b41c4SGunnar Mills 
272ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class
282ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server;
290129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server;
302ce7da29SGunnar Mills 
312ce7da29SGunnar Mills using namespace phosphor::logging;
32dcbfa04aSSaqib Khan using namespace sdbusplus::xyz::openbmc_project::Software::Version::Error;
332ab9b109SJayanth Othayoth using namespace phosphor::software::image;
3435e83f3eSSaqib Khan namespace fs = std::experimental::filesystem;
3535e83f3eSSaqib Khan 
36e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg)
37ec1b41c4SGunnar Mills {
3884a0e693SSaqib Khan 
3984a0e693SSaqib Khan     using SVersion = server::Version;
4084a0e693SSaqib Khan     using VersionPurpose = SVersion::VersionPurpose;
419a782243SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
4284a0e693SSaqib Khan     namespace mesg = sdbusplus::message;
4384a0e693SSaqib Khan     namespace variant_ns = mesg::variant_ns;
4484a0e693SSaqib Khan 
4584a0e693SSaqib Khan     mesg::object_path objPath;
4684a0e693SSaqib Khan     auto purpose = VersionPurpose::Unknown;
47705f1bfcSSaqib Khan     std::string version;
482285fe0fSAdriana Kobylak     std::map<std::string, std::map<std::string, mesg::variant<std::string>>>
492285fe0fSAdriana Kobylak         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     }
872285fe0fSAdriana Kobylak     if (version.empty() || filePath.empty() ||
8884a0e693SSaqib Khan         purpose == VersionPurpose::Unknown)
892ce7da29SGunnar Mills     {
90e75d10f5SPatrick Williams         return;
912ce7da29SGunnar Mills     }
922ce7da29SGunnar Mills 
932ce7da29SGunnar Mills     // Version id is the last item in the path
942ce7da29SGunnar Mills     auto pos = path.rfind("/");
952ce7da29SGunnar Mills     if (pos == std::string::npos)
962ce7da29SGunnar Mills     {
972ce7da29SGunnar Mills         log<level::ERR>("No version id found in object path",
98596466b8SAdriana Kobylak                         entry("OBJPATH=%s", path.c_str()));
99e75d10f5SPatrick Williams         return;
1002ce7da29SGunnar Mills     }
1012ce7da29SGunnar Mills 
1022ce7da29SGunnar Mills     auto versionId = path.substr(pos + 1);
1032ce7da29SGunnar Mills 
104e75d10f5SPatrick Williams     if (activations.find(versionId) == activations.end())
1052ce7da29SGunnar Mills     {
10635e83f3eSSaqib Khan         // Determine the Activation state by processing the given image dir.
10735e83f3eSSaqib Khan         auto activationState = server::Activation::Activations::Invalid;
1089a782243SGunnar Mills         ItemUpdater::ActivationStatus result =
1099a782243SGunnar Mills             ItemUpdater::validateSquashFSImage(filePath);
11043b25cdeSGunnar Mills         AssociationList associations = {};
11143b25cdeSGunnar Mills 
11235e83f3eSSaqib Khan         if (result == ItemUpdater::ActivationStatus::ready)
11335e83f3eSSaqib Khan         {
11435e83f3eSSaqib Khan             activationState = server::Activation::Activations::Ready;
115b60add1eSGunnar Mills             // Create an association to the BMC inventory item
1162285fe0fSAdriana Kobylak             associations.emplace_back(
1172285fe0fSAdriana Kobylak                 std::make_tuple(ACTIVATION_FWD_ASSOCIATION,
1182285fe0fSAdriana Kobylak                                 ACTIVATION_REV_ASSOCIATION, bmcInventoryPath));
11943b25cdeSGunnar Mills         }
120b60add1eSGunnar Mills 
121ee13e831SSaqib Khan         activations.insert(std::make_pair(
122ee13e831SSaqib Khan             versionId,
1232285fe0fSAdriana Kobylak             std::make_unique<Activation>(bus, path, *this, versionId,
1242285fe0fSAdriana Kobylak                                          activationState, associations)));
1254254beceSMichael Tritz 
126ee13e831SSaqib Khan         auto versionPtr = std::make_unique<VersionClass>(
1272285fe0fSAdriana Kobylak             bus, path, version, purpose, filePath,
1282285fe0fSAdriana Kobylak             std::bind(&ItemUpdater::erase, this, std::placeholders::_1));
129ee13e831SSaqib Khan         versionPtr->deleteObject =
1302285fe0fSAdriana Kobylak             std::make_unique<phosphor::software::manager::Delete>(bus, path,
1312285fe0fSAdriana Kobylak                                                                   *versionPtr);
132ee13e831SSaqib Khan         versions.insert(std::make_pair(versionId, std::move(versionPtr)));
1332ce7da29SGunnar Mills     }
134e75d10f5SPatrick Williams     return;
135ec1b41c4SGunnar Mills }
136ec1b41c4SGunnar Mills 
137ba239881SSaqib Khan void ItemUpdater::processBMCImage()
138ba239881SSaqib Khan {
13988e8a325SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
140269bff30SLei YU 
141269bff30SLei YU     // Check MEDIA_DIR and create if it does not exist
142269bff30SLei YU     try
143269bff30SLei YU     {
144269bff30SLei YU         if (!fs::is_directory(MEDIA_DIR))
145269bff30SLei YU         {
146269bff30SLei YU             fs::create_directory(MEDIA_DIR);
147269bff30SLei YU         }
148269bff30SLei YU     }
149269bff30SLei YU     catch (const fs::filesystem_error& e)
150269bff30SLei YU     {
151269bff30SLei YU         log<level::ERR>("Failed to prepare dir", entry("ERR=%s", e.what()));
152269bff30SLei YU         return;
153269bff30SLei YU     }
154269bff30SLei YU 
15588e8a325SGunnar Mills     // Read os-release from /etc/ to get the functional BMC version
15688e8a325SGunnar Mills     auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE);
15788e8a325SGunnar Mills 
1581eef62deSSaqib Khan     // Read os-release from folders under /media/ to get
1591eef62deSSaqib Khan     // BMC Software Versions.
1601eef62deSSaqib Khan     for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
1611eef62deSSaqib Khan     {
1621eef62deSSaqib Khan         auto activationState = server::Activation::Activations::Active;
1636fab70daSSaqib Khan         static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX);
1641eef62deSSaqib Khan 
1651eef62deSSaqib Khan         // Check if the BMC_RO_PREFIXis the prefix of the iter.path
1662285fe0fSAdriana Kobylak         if (0 ==
1672285fe0fSAdriana Kobylak             iter.path().native().compare(0, BMC_RO_PREFIX_LEN, BMC_ROFS_PREFIX))
1681eef62deSSaqib Khan         {
169021c365bSSaqib Khan             // The versionId is extracted from the path
170021c365bSSaqib Khan             // for example /media/ro-2a1022fe.
171021c365bSSaqib Khan             auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
1721eef62deSSaqib Khan             auto osRelease = iter.path() / OS_RELEASE_FILE;
1731eef62deSSaqib Khan             if (!fs::is_regular_file(osRelease))
1741eef62deSSaqib Khan             {
1752285fe0fSAdriana Kobylak                 log<level::ERR>(
1762285fe0fSAdriana Kobylak                     "Failed to read osRelease",
177596466b8SAdriana Kobylak                     entry("FILENAME=%s", osRelease.string().c_str()));
178021c365bSSaqib Khan                 ItemUpdater::erase(id);
179021c365bSSaqib Khan                 continue;
1801eef62deSSaqib Khan             }
18188e8a325SGunnar Mills             auto version = VersionClass::getBMCVersion(osRelease);
1821eef62deSSaqib Khan             if (version.empty())
1831eef62deSSaqib Khan             {
1842285fe0fSAdriana Kobylak                 log<level::ERR>(
1852285fe0fSAdriana Kobylak                     "Failed to read version from osRelease",
186596466b8SAdriana Kobylak                     entry("FILENAME=%s", osRelease.string().c_str()));
1871eef62deSSaqib Khan                 activationState = server::Activation::Activations::Invalid;
1881eef62deSSaqib Khan             }
189021c365bSSaqib Khan 
1901eef62deSSaqib Khan             auto purpose = server::Version::VersionPurpose::BMC;
1911eef62deSSaqib Khan             auto path = fs::path(SOFTWARE_OBJPATH) / id;
1921eef62deSSaqib Khan 
193269bff30SLei YU             // Create functional association if this is the functional
194269bff30SLei YU             // version
19588e8a325SGunnar Mills             if (version.compare(functionalVersion) == 0)
19688e8a325SGunnar Mills             {
19788e8a325SGunnar Mills                 createFunctionalAssociation(path);
19888e8a325SGunnar Mills             }
19988e8a325SGunnar Mills 
20043b25cdeSGunnar Mills             AssociationList associations = {};
20143b25cdeSGunnar Mills 
20243b25cdeSGunnar Mills             if (activationState == server::Activation::Activations::Active)
20343b25cdeSGunnar Mills             {
20443b25cdeSGunnar Mills                 // Create an association to the BMC inventory item
20543b25cdeSGunnar Mills                 associations.emplace_back(std::make_tuple(
2062285fe0fSAdriana Kobylak                     ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
20743b25cdeSGunnar Mills                     bmcInventoryPath));
20843b25cdeSGunnar Mills 
20943b25cdeSGunnar Mills                 // Create an active association since this image is active
21043b25cdeSGunnar Mills                 createActiveAssociation(path);
21143b25cdeSGunnar Mills             }
21243b25cdeSGunnar Mills 
213ee590c74SAdriana Kobylak             // Create Version instance for this version.
214ee590c74SAdriana Kobylak             auto versionPtr = std::make_unique<VersionClass>(
2152285fe0fSAdriana Kobylak                 bus, path, version, purpose, "",
2162285fe0fSAdriana Kobylak                 std::bind(&ItemUpdater::erase, this, std::placeholders::_1));
217ee590c74SAdriana Kobylak             auto isVersionFunctional = versionPtr->isFunctional();
218ee13e831SSaqib Khan             if (!isVersionFunctional)
219ee13e831SSaqib Khan             {
220ee13e831SSaqib Khan                 versionPtr->deleteObject =
221ee13e831SSaqib Khan                     std::make_unique<phosphor::software::manager::Delete>(
222ee13e831SSaqib Khan                         bus, path, *versionPtr);
223ee13e831SSaqib Khan             }
2242285fe0fSAdriana Kobylak             versions.insert(std::make_pair(id, std::move(versionPtr)));
225ee590c74SAdriana Kobylak 
2261eef62deSSaqib Khan             // Create Activation instance for this version.
227ee13e831SSaqib Khan             activations.insert(std::make_pair(
2282285fe0fSAdriana Kobylak                 id, std::make_unique<Activation>(
2292285fe0fSAdriana Kobylak                         bus, path, *this, id, activationState, associations)));
2301eef62deSSaqib Khan 
231269bff30SLei YU             // If Active, create RedundancyPriority instance for this
232269bff30SLei YU             // version.
2331eef62deSSaqib Khan             if (activationState == server::Activation::Activations::Active)
2341eef62deSSaqib Khan             {
2351eef62deSSaqib Khan                 uint8_t priority = std::numeric_limits<uint8_t>::max();
2361eef62deSSaqib Khan                 if (!restoreFromFile(id, priority))
2371eef62deSSaqib Khan                 {
238ee590c74SAdriana Kobylak                     if (isVersionFunctional)
239ee590c74SAdriana Kobylak                     {
240ee590c74SAdriana Kobylak                         priority = 0;
241ee590c74SAdriana Kobylak                     }
242ee590c74SAdriana Kobylak                     else
243ee590c74SAdriana Kobylak                     {
2441eef62deSSaqib Khan                         log<level::ERR>("Unable to restore priority from file.",
245596466b8SAdriana Kobylak                                         entry("VERSIONID=%s", id.c_str()));
2461eef62deSSaqib Khan                     }
247ee590c74SAdriana Kobylak                 }
2481eef62deSSaqib Khan                 activations.find(id)->second->redundancyPriority =
2491eef62deSSaqib Khan                     std::make_unique<RedundancyPriority>(
2502285fe0fSAdriana Kobylak                         bus, path, *(activations.find(id)->second), priority,
251b77551cdSAdriana Kobylak                         false);
2521eef62deSSaqib Khan             }
2531eef62deSSaqib Khan         }
2541eef62deSSaqib Khan     }
255dcbfa04aSSaqib Khan 
256dcbfa04aSSaqib Khan     // If there is no ubi volume for bmc version then read the /etc/os-release
257dcbfa04aSSaqib Khan     // and create rofs-<versionId> under /media
258dcbfa04aSSaqib Khan     if (activations.size() == 0)
259dcbfa04aSSaqib Khan     {
260d16bcbd5SGunnar Mills         auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE);
261dcbfa04aSSaqib Khan         auto id = phosphor::software::manager::Version::getId(version);
262dcbfa04aSSaqib Khan         auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/";
263dcbfa04aSSaqib Khan         try
264dcbfa04aSSaqib Khan         {
265dcbfa04aSSaqib Khan             if (!fs::is_directory(versionFileDir))
266dcbfa04aSSaqib Khan             {
267dcbfa04aSSaqib Khan                 fs::create_directories(versionFileDir);
268dcbfa04aSSaqib Khan             }
269dcbfa04aSSaqib Khan             auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE;
270dcbfa04aSSaqib Khan             fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath);
271dcbfa04aSSaqib Khan             ItemUpdater::processBMCImage();
272dcbfa04aSSaqib Khan         }
273dcbfa04aSSaqib Khan         catch (const std::exception& e)
274dcbfa04aSSaqib Khan         {
275dcbfa04aSSaqib Khan             log<level::ERR>(e.what());
276dcbfa04aSSaqib Khan         }
277dcbfa04aSSaqib Khan     }
278eaa1ee05SEddie James 
279eaa1ee05SEddie James     mirrorUbootToAlt();
280ba239881SSaqib Khan     return;
281ba239881SSaqib Khan }
282ba239881SSaqib Khan 
2833526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId)
2843526ef73SLeonel Gonzalez {
2856d873715SEddie James     // Find entry in versions map
2866d873715SEddie James     auto it = versions.find(entryId);
2876d873715SEddie James     if (it != versions.end())
2886d873715SEddie James     {
2890f88b5afSLei YU         if (it->second->isFunctional() && ACTIVE_BMC_MAX_ALLOWED > 1)
2906d873715SEddie James         {
2912285fe0fSAdriana Kobylak             log<level::ERR>("Error: Version is currently running on the BMC. "
2922285fe0fSAdriana Kobylak                             "Unable to remove.",
2932285fe0fSAdriana Kobylak                             entry("VERSIONID=%s", entryId.c_str()));
2946d873715SEddie James             return;
2956d873715SEddie James         }
2966d873715SEddie James 
2976d873715SEddie James         // Delete ReadOnly partitions if it's not active
2983526ef73SLeonel Gonzalez         removeReadOnlyPartition(entryId);
2991eef62deSSaqib Khan         removeFile(entryId);
300ee13e831SSaqib Khan 
301ee13e831SSaqib Khan         // Removing entry in versions map
302ee13e831SSaqib Khan         this->versions.erase(entryId);
3036d873715SEddie James     }
3046d873715SEddie James     else
3056d873715SEddie James     {
3066d873715SEddie James         // Delete ReadOnly partitions even if we can't find the version
3076d873715SEddie James         removeReadOnlyPartition(entryId);
3086d873715SEddie James         removeFile(entryId);
3096d873715SEddie James 
3102285fe0fSAdriana Kobylak         log<level::ERR>("Error: Failed to find version in item updater "
3112285fe0fSAdriana Kobylak                         "versions map. Unable to remove.",
3122285fe0fSAdriana Kobylak                         entry("VERSIONID=%s", entryId.c_str()));
3136d873715SEddie James     }
3141eef62deSSaqib Khan 
31556aaf454SLei YU     helper.clearEntry(entryId);
3163526ef73SLeonel Gonzalez 
3173526ef73SLeonel Gonzalez     // Removing entry in activations map
3183526ef73SLeonel Gonzalez     auto ita = activations.find(entryId);
3193526ef73SLeonel Gonzalez     if (ita == activations.end())
3203526ef73SLeonel Gonzalez     {
3212285fe0fSAdriana Kobylak         log<level::ERR>("Error: Failed to find version in item updater "
3222285fe0fSAdriana Kobylak                         "activations map. Unable to remove.",
3232285fe0fSAdriana Kobylak                         entry("VERSIONID=%s", entryId.c_str()));
3243526ef73SLeonel Gonzalez     }
325ee13e831SSaqib Khan     else
326ee13e831SSaqib Khan     {
3273526ef73SLeonel Gonzalez         this->activations.erase(entryId);
328ee13e831SSaqib Khan     }
32949446ae9SSaqib Khan     ItemUpdater::resetUbootEnvVars();
330ee13e831SSaqib Khan     return;
3313526ef73SLeonel Gonzalez }
3323526ef73SLeonel Gonzalez 
333bc1bf3afSMichael Tritz void ItemUpdater::deleteAll()
334bc1bf3afSMichael Tritz {
33583cd21fbSAdriana Kobylak     std::vector<std::string> deletableVersions;
33683cd21fbSAdriana Kobylak 
337bc1bf3afSMichael Tritz     for (const auto& versionIt : versions)
338bc1bf3afSMichael Tritz     {
339bc1bf3afSMichael Tritz         if (!versionIt.second->isFunctional())
340bc1bf3afSMichael Tritz         {
34183cd21fbSAdriana Kobylak             deletableVersions.push_back(versionIt.first);
342bc1bf3afSMichael Tritz         }
343bc1bf3afSMichael Tritz     }
344bc1bf3afSMichael Tritz 
34583cd21fbSAdriana Kobylak     for (const auto& deletableIt : deletableVersions)
34683cd21fbSAdriana Kobylak     {
34783cd21fbSAdriana Kobylak         ItemUpdater::erase(deletableIt);
34883cd21fbSAdriana Kobylak     }
34983cd21fbSAdriana Kobylak 
35056aaf454SLei YU     helper.cleanup();
351bc1bf3afSMichael Tritz }
352bc1bf3afSMichael Tritz 
3532285fe0fSAdriana Kobylak ItemUpdater::ActivationStatus
3542285fe0fSAdriana Kobylak     ItemUpdater::validateSquashFSImage(const std::string& filePath)
35535e83f3eSSaqib Khan {
356b1cfdf99SMichael Tritz     bool invalid = false;
35735e83f3eSSaqib Khan 
358b1cfdf99SMichael Tritz     for (auto& bmcImage : bmcImages)
359b1cfdf99SMichael Tritz     {
36019177d3eSSaqib Khan         fs::path file(filePath);
36135e83f3eSSaqib Khan         file /= bmcImage;
36235e83f3eSSaqib Khan         std::ifstream efile(file.c_str());
363b1cfdf99SMichael Tritz         if (efile.good() != 1)
36435e83f3eSSaqib Khan         {
365b1cfdf99SMichael Tritz             log<level::ERR>("Failed to find the BMC image.",
366b1cfdf99SMichael Tritz                             entry("IMAGE=%s", bmcImage.c_str()));
367b1cfdf99SMichael Tritz             invalid = true;
36835e83f3eSSaqib Khan         }
369b1cfdf99SMichael Tritz     }
370b1cfdf99SMichael Tritz 
371b1cfdf99SMichael Tritz     if (invalid)
37235e83f3eSSaqib Khan     {
37335e83f3eSSaqib Khan         return ItemUpdater::ActivationStatus::invalid;
37435e83f3eSSaqib Khan     }
375b1cfdf99SMichael Tritz 
376b1cfdf99SMichael Tritz     return ItemUpdater::ActivationStatus::ready;
37735e83f3eSSaqib Khan }
37835e83f3eSSaqib Khan 
379bbcb7be1SAdriana Kobylak void ItemUpdater::savePriority(const std::string& versionId, uint8_t value)
380bbcb7be1SAdriana Kobylak {
381bbcb7be1SAdriana Kobylak     storeToFile(versionId, value);
382bbcb7be1SAdriana Kobylak     helper.setEntry(versionId, value);
383bbcb7be1SAdriana Kobylak }
384bbcb7be1SAdriana Kobylak 
385b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
3864c1aec09SSaqib Khan {
387b77551cdSAdriana Kobylak     std::map<std::string, uint8_t> priorityMap;
388b77551cdSAdriana Kobylak 
389b77551cdSAdriana Kobylak     // Insert the requested version and priority, it may not exist yet.
390b77551cdSAdriana Kobylak     priorityMap.insert(std::make_pair(versionId, value));
391b77551cdSAdriana Kobylak 
3924c1aec09SSaqib Khan     for (const auto& intf : activations)
3934c1aec09SSaqib Khan     {
3944c1aec09SSaqib Khan         if (intf.second->redundancyPriority)
3954c1aec09SSaqib Khan         {
396b77551cdSAdriana Kobylak             priorityMap.insert(std::make_pair(
3972285fe0fSAdriana Kobylak                 intf.first, intf.second->redundancyPriority.get()->priority()));
398b77551cdSAdriana Kobylak         }
399b77551cdSAdriana Kobylak     }
400b77551cdSAdriana Kobylak 
401b77551cdSAdriana Kobylak     // Lambda function to compare 2 priority values, use <= to allow duplicates
4022285fe0fSAdriana Kobylak     typedef std::function<bool(std::pair<std::string, uint8_t>,
4032285fe0fSAdriana Kobylak                                std::pair<std::string, uint8_t>)>
4042285fe0fSAdriana Kobylak         cmpPriority;
4052285fe0fSAdriana Kobylak     cmpPriority cmpPriorityFunc =
4062285fe0fSAdriana Kobylak         [](std::pair<std::string, uint8_t> priority1,
4072285fe0fSAdriana Kobylak            std::pair<std::string, uint8_t> priority2) {
408b77551cdSAdriana Kobylak             return priority1.second <= priority2.second;
409b77551cdSAdriana Kobylak         };
410b77551cdSAdriana Kobylak 
411b77551cdSAdriana Kobylak     // Sort versions by ascending priority
412b77551cdSAdriana Kobylak     std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet(
413b77551cdSAdriana Kobylak         priorityMap.begin(), priorityMap.end(), cmpPriorityFunc);
414b77551cdSAdriana Kobylak 
415b77551cdSAdriana Kobylak     auto freePriorityValue = value;
416b77551cdSAdriana Kobylak     for (auto& element : prioritySet)
417b77551cdSAdriana Kobylak     {
418b77551cdSAdriana Kobylak         if (element.first == versionId)
419b77551cdSAdriana Kobylak         {
420b77551cdSAdriana Kobylak             continue;
421b77551cdSAdriana Kobylak         }
422b77551cdSAdriana Kobylak         if (element.second == freePriorityValue)
423b77551cdSAdriana Kobylak         {
424b77551cdSAdriana Kobylak             ++freePriorityValue;
425b77551cdSAdriana Kobylak             auto it = activations.find(element.first);
426b77551cdSAdriana Kobylak             it->second->redundancyPriority.get()->sdbusPriority(
427b77551cdSAdriana Kobylak                 freePriorityValue);
4284c1aec09SSaqib Khan         }
4294c1aec09SSaqib Khan     }
430b77551cdSAdriana Kobylak 
431b77551cdSAdriana Kobylak     auto lowestVersion = prioritySet.begin()->first;
432b77551cdSAdriana Kobylak     if (value == prioritySet.begin()->second)
433b77551cdSAdriana Kobylak     {
434b77551cdSAdriana Kobylak         lowestVersion = versionId;
4354c1aec09SSaqib Khan     }
436b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestVersion);
4374c1aec09SSaqib Khan }
4384c1aec09SSaqib Khan 
43937a59043SMichael Tritz void ItemUpdater::reset()
44037a59043SMichael Tritz {
44156aaf454SLei YU     helper.factoryReset();
44237a59043SMichael Tritz 
44337a59043SMichael Tritz     log<level::INFO>("BMC factory reset will take effect upon reboot.");
44437a59043SMichael Tritz }
44537a59043SMichael Tritz 
4463526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId)
4473526ef73SLeonel Gonzalez {
44856aaf454SLei YU     helper.removeVersion(versionId);
4493526ef73SLeonel Gonzalez }
4503526ef73SLeonel Gonzalez 
4510129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value)
4520129d926SMichael Tritz {
4530129d926SMichael Tritz     // enabling field mode is intended to be one way: false -> true
4540129d926SMichael Tritz     if (value && !control::FieldMode::fieldModeEnabled())
4550129d926SMichael Tritz     {
4560129d926SMichael Tritz         control::FieldMode::fieldModeEnabled(value);
4570129d926SMichael Tritz 
45856aaf454SLei YU         helper.enableFieldMode();
4590129d926SMichael Tritz     }
4600129d926SMichael Tritz 
4610129d926SMichael Tritz     return control::FieldMode::fieldModeEnabled();
4620129d926SMichael Tritz }
4630129d926SMichael Tritz 
4640129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus()
4650129d926SMichael Tritz {
466ff0b421dSMichael Tritz     std::ifstream input("/dev/mtd/u-boot-env");
4670129d926SMichael Tritz     std::string envVar;
4680129d926SMichael Tritz     std::getline(input, envVar);
4690129d926SMichael Tritz 
4700129d926SMichael Tritz     if (envVar.find("fieldmode=true") != std::string::npos)
4710129d926SMichael Tritz     {
4720129d926SMichael Tritz         ItemUpdater::fieldModeEnabled(true);
4730129d926SMichael Tritz     }
4740129d926SMichael Tritz }
4750129d926SMichael Tritz 
476b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath()
477b60add1eSGunnar Mills {
478b60add1eSGunnar Mills     auto depth = 0;
4792285fe0fSAdriana Kobylak     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
4802285fe0fSAdriana Kobylak                                           MAPPER_INTERFACE, "GetSubTreePaths");
481b60add1eSGunnar Mills 
4821254c628SAdriana Kobylak     mapperCall.append(INVENTORY_PATH);
483b60add1eSGunnar Mills     mapperCall.append(depth);
4841254c628SAdriana Kobylak     std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE};
485b60add1eSGunnar Mills     mapperCall.append(filter);
486b60add1eSGunnar Mills 
48787c78173SEd Tanous     try
488b60add1eSGunnar Mills     {
48987c78173SEd Tanous         auto response = bus.call(mapperCall);
490b60add1eSGunnar Mills 
491b60add1eSGunnar Mills         using ObjectPaths = std::vector<std::string>;
492b60add1eSGunnar Mills         ObjectPaths result;
493b60add1eSGunnar Mills         response.read(result);
494b60add1eSGunnar Mills 
4951254c628SAdriana Kobylak         if (!result.empty())
496b60add1eSGunnar Mills         {
4971254c628SAdriana Kobylak             bmcInventoryPath = result.front();
498b60add1eSGunnar Mills         }
49987c78173SEd Tanous     }
50087c78173SEd Tanous     catch (const sdbusplus::exception::SdBusError& e)
50187c78173SEd Tanous     {
50287c78173SEd Tanous         log<level::ERR>("Error in mapper GetSubTreePath");
50387c78173SEd Tanous         return;
50487c78173SEd Tanous     }
505b60add1eSGunnar Mills 
506b60add1eSGunnar Mills     return;
507b60add1eSGunnar Mills }
508b60add1eSGunnar Mills 
509f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path)
510ded875dcSGunnar Mills {
5112285fe0fSAdriana Kobylak     assocs.emplace_back(
5122285fe0fSAdriana Kobylak         std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
513ded875dcSGunnar Mills     associations(assocs);
514ded875dcSGunnar Mills }
515ded875dcSGunnar Mills 
51688e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path)
51788e8a325SGunnar Mills {
51888e8a325SGunnar Mills     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
5192285fe0fSAdriana Kobylak                                         FUNCTIONAL_REV_ASSOCIATION, path));
52088e8a325SGunnar Mills     associations(assocs);
52188e8a325SGunnar Mills }
52288e8a325SGunnar Mills 
523f10b2326SGunnar Mills void ItemUpdater::removeActiveAssociation(const std::string& path)
524ded875dcSGunnar Mills {
525ded875dcSGunnar Mills     for (auto iter = assocs.begin(); iter != assocs.end();)
526ded875dcSGunnar Mills     {
52788e8a325SGunnar Mills         // Since there could be multiple associations to the same path,
52888e8a325SGunnar Mills         // only remove ones that have an active forward association.
52988e8a325SGunnar Mills         if ((std::get<0>(*iter)).compare(ACTIVE_FWD_ASSOCIATION) == 0 &&
53088e8a325SGunnar Mills             (std::get<2>(*iter)).compare(path) == 0)
531ded875dcSGunnar Mills         {
532ded875dcSGunnar Mills             iter = assocs.erase(iter);
533ded875dcSGunnar Mills             associations(assocs);
534ded875dcSGunnar Mills         }
535ded875dcSGunnar Mills         else
536ded875dcSGunnar Mills         {
537ded875dcSGunnar Mills             ++iter;
538ded875dcSGunnar Mills         }
539ded875dcSGunnar Mills     }
540ded875dcSGunnar Mills }
541ded875dcSGunnar Mills 
542b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value)
543b9da6634SSaqib Khan {
544b9da6634SSaqib Khan     for (const auto& intf : activations)
545b9da6634SSaqib Khan     {
546b9da6634SSaqib Khan         if (intf.second->redundancyPriority)
547b9da6634SSaqib Khan         {
548b9da6634SSaqib Khan             if (intf.second->redundancyPriority.get()->priority() < value)
549b9da6634SSaqib Khan             {
550b9da6634SSaqib Khan                 return false;
551b9da6634SSaqib Khan             }
552b9da6634SSaqib Khan         }
553b9da6634SSaqib Khan     }
554b9da6634SSaqib Khan     return true;
555b9da6634SSaqib Khan }
556b9da6634SSaqib Khan 
557b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId)
558b77551cdSAdriana Kobylak {
55956aaf454SLei YU     helper.updateUbootVersionId(versionId);
560b77551cdSAdriana Kobylak }
561b77551cdSAdriana Kobylak 
56249446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars()
56349446ae9SSaqib Khan {
56449446ae9SSaqib Khan     decltype(activations.begin()->second->redundancyPriority.get()->priority())
56549446ae9SSaqib Khan         lowestPriority = std::numeric_limits<uint8_t>::max();
56649446ae9SSaqib Khan     decltype(activations.begin()->second->versionId) lowestPriorityVersion;
56749446ae9SSaqib Khan     for (const auto& intf : activations)
56849446ae9SSaqib Khan     {
56949446ae9SSaqib Khan         if (!intf.second->redundancyPriority.get())
57049446ae9SSaqib Khan         {
57149446ae9SSaqib Khan             // Skip this version if the redundancyPriority is not initialized.
57249446ae9SSaqib Khan             continue;
57349446ae9SSaqib Khan         }
57449446ae9SSaqib Khan 
5752285fe0fSAdriana Kobylak         if (intf.second->redundancyPriority.get()->priority() <= lowestPriority)
57649446ae9SSaqib Khan         {
57749446ae9SSaqib Khan             lowestPriority = intf.second->redundancyPriority.get()->priority();
57849446ae9SSaqib Khan             lowestPriorityVersion = intf.second->versionId;
57949446ae9SSaqib Khan         }
58049446ae9SSaqib Khan     }
58149446ae9SSaqib Khan 
582f0382c35SSaqib Khan     // Update the U-boot environment variable to point to the lowest priority
583b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestPriorityVersion);
58449446ae9SSaqib Khan }
58549446ae9SSaqib Khan 
586*a6963590SAdriana Kobylak void ItemUpdater::freeSpace(Activation& caller)
587204e1e74SAdriana Kobylak {
588204e1e74SAdriana Kobylak     //  Versions with the highest priority in front
589204e1e74SAdriana Kobylak     std::priority_queue<std::pair<int, std::string>,
590204e1e74SAdriana Kobylak                         std::vector<std::pair<int, std::string>>,
5912285fe0fSAdriana Kobylak                         std::less<std::pair<int, std::string>>>
5922285fe0fSAdriana Kobylak         versionsPQ;
593204e1e74SAdriana Kobylak 
594204e1e74SAdriana Kobylak     std::size_t count = 0;
595204e1e74SAdriana Kobylak     for (const auto& iter : activations)
596204e1e74SAdriana Kobylak     {
597204e1e74SAdriana Kobylak         if ((iter.second.get()->activation() ==
598204e1e74SAdriana Kobylak              server::Activation::Activations::Active) ||
599204e1e74SAdriana Kobylak             (iter.second.get()->activation() ==
600204e1e74SAdriana Kobylak              server::Activation::Activations::Failed))
601204e1e74SAdriana Kobylak         {
602204e1e74SAdriana Kobylak             count++;
603204e1e74SAdriana Kobylak             // Don't put the functional version on the queue since we can't
604204e1e74SAdriana Kobylak             // remove the "running" BMC version.
6050f88b5afSLei YU             // If ACTIVE_BMC_MAX_ALLOWED <= 1, there is only one active BMC,
6060f88b5afSLei YU             // so remove functional version as well.
607*a6963590SAdriana Kobylak             // Don't delete the the Activation object that called this function.
608*a6963590SAdriana Kobylak             if ((versions.find(iter.second->versionId)
609*a6963590SAdriana Kobylak                      ->second->isFunctional() &&
610*a6963590SAdriana Kobylak                  ACTIVE_BMC_MAX_ALLOWED > 1) ||
611*a6963590SAdriana Kobylak                 (iter.second->versionId == caller.versionId))
612204e1e74SAdriana Kobylak             {
613204e1e74SAdriana Kobylak                 continue;
614204e1e74SAdriana Kobylak             }
615*a6963590SAdriana Kobylak 
616*a6963590SAdriana Kobylak             // Failed activations don't have priority, assign them a large value
617*a6963590SAdriana Kobylak             // for sorting purposes.
618*a6963590SAdriana Kobylak             auto priority = 999;
619*a6963590SAdriana Kobylak             if (iter.second.get()->activation() ==
620*a6963590SAdriana Kobylak                 server::Activation::Activations::Active)
621*a6963590SAdriana Kobylak             {
622*a6963590SAdriana Kobylak                 priority = iter.second->redundancyPriority.get()->priority();
623*a6963590SAdriana Kobylak             }
624*a6963590SAdriana Kobylak 
625*a6963590SAdriana Kobylak             versionsPQ.push(std::make_pair(priority, iter.second->versionId));
626204e1e74SAdriana Kobylak         }
627204e1e74SAdriana Kobylak     }
628204e1e74SAdriana Kobylak 
629204e1e74SAdriana Kobylak     // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1,
630204e1e74SAdriana Kobylak     // remove the highest priority one(s).
631204e1e74SAdriana Kobylak     while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty()))
632204e1e74SAdriana Kobylak     {
633204e1e74SAdriana Kobylak         erase(versionsPQ.top().second);
634204e1e74SAdriana Kobylak         versionsPQ.pop();
635204e1e74SAdriana Kobylak         count--;
636204e1e74SAdriana Kobylak     }
637204e1e74SAdriana Kobylak }
638204e1e74SAdriana Kobylak 
639eaa1ee05SEddie James void ItemUpdater::mirrorUbootToAlt()
640eaa1ee05SEddie James {
64156aaf454SLei YU     helper.mirrorAlt();
642eaa1ee05SEddie James }
643eaa1ee05SEddie James 
644ec1b41c4SGunnar Mills } // namespace updater
645ec1b41c4SGunnar Mills } // namespace software
646ec1b41c4SGunnar Mills } // namespace phosphor
647