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>
1843699ca7SAdriana Kobylak #include <xyz/openbmc_project/Software/Image/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;
3243699ca7SAdriana Kobylak using namespace sdbusplus::xyz::openbmc_project::Software::Image::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     {
327*991af7ecSAdriana Kobylak         removeAssociations(ita->second->path);
3283526ef73SLeonel Gonzalez         this->activations.erase(entryId);
329ee13e831SSaqib Khan     }
33049446ae9SSaqib Khan     ItemUpdater::resetUbootEnvVars();
331ee13e831SSaqib Khan     return;
3323526ef73SLeonel Gonzalez }
3333526ef73SLeonel Gonzalez 
334bc1bf3afSMichael Tritz void ItemUpdater::deleteAll()
335bc1bf3afSMichael Tritz {
33683cd21fbSAdriana Kobylak     std::vector<std::string> deletableVersions;
33783cd21fbSAdriana Kobylak 
338bc1bf3afSMichael Tritz     for (const auto& versionIt : versions)
339bc1bf3afSMichael Tritz     {
340bc1bf3afSMichael Tritz         if (!versionIt.second->isFunctional())
341bc1bf3afSMichael Tritz         {
34283cd21fbSAdriana Kobylak             deletableVersions.push_back(versionIt.first);
343bc1bf3afSMichael Tritz         }
344bc1bf3afSMichael Tritz     }
345bc1bf3afSMichael Tritz 
34683cd21fbSAdriana Kobylak     for (const auto& deletableIt : deletableVersions)
34783cd21fbSAdriana Kobylak     {
34883cd21fbSAdriana Kobylak         ItemUpdater::erase(deletableIt);
34983cd21fbSAdriana Kobylak     }
35083cd21fbSAdriana Kobylak 
35156aaf454SLei YU     helper.cleanup();
352bc1bf3afSMichael Tritz }
353bc1bf3afSMichael Tritz 
3542285fe0fSAdriana Kobylak ItemUpdater::ActivationStatus
3552285fe0fSAdriana Kobylak     ItemUpdater::validateSquashFSImage(const std::string& filePath)
35635e83f3eSSaqib Khan {
357b1cfdf99SMichael Tritz     bool invalid = false;
35835e83f3eSSaqib Khan 
359b1cfdf99SMichael Tritz     for (auto& bmcImage : bmcImages)
360b1cfdf99SMichael Tritz     {
36119177d3eSSaqib Khan         fs::path file(filePath);
36235e83f3eSSaqib Khan         file /= bmcImage;
36335e83f3eSSaqib Khan         std::ifstream efile(file.c_str());
364b1cfdf99SMichael Tritz         if (efile.good() != 1)
36535e83f3eSSaqib Khan         {
366b1cfdf99SMichael Tritz             log<level::ERR>("Failed to find the BMC image.",
367b1cfdf99SMichael Tritz                             entry("IMAGE=%s", bmcImage.c_str()));
368b1cfdf99SMichael Tritz             invalid = true;
36935e83f3eSSaqib Khan         }
370b1cfdf99SMichael Tritz     }
371b1cfdf99SMichael Tritz 
372b1cfdf99SMichael Tritz     if (invalid)
37335e83f3eSSaqib Khan     {
37435e83f3eSSaqib Khan         return ItemUpdater::ActivationStatus::invalid;
37535e83f3eSSaqib Khan     }
376b1cfdf99SMichael Tritz 
377b1cfdf99SMichael Tritz     return ItemUpdater::ActivationStatus::ready;
37835e83f3eSSaqib Khan }
37935e83f3eSSaqib Khan 
380bbcb7be1SAdriana Kobylak void ItemUpdater::savePriority(const std::string& versionId, uint8_t value)
381bbcb7be1SAdriana Kobylak {
382bbcb7be1SAdriana Kobylak     storeToFile(versionId, value);
383bbcb7be1SAdriana Kobylak     helper.setEntry(versionId, value);
384bbcb7be1SAdriana Kobylak }
385bbcb7be1SAdriana Kobylak 
386b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
3874c1aec09SSaqib Khan {
388b77551cdSAdriana Kobylak     std::map<std::string, uint8_t> priorityMap;
389b77551cdSAdriana Kobylak 
390b77551cdSAdriana Kobylak     // Insert the requested version and priority, it may not exist yet.
391b77551cdSAdriana Kobylak     priorityMap.insert(std::make_pair(versionId, value));
392b77551cdSAdriana Kobylak 
3934c1aec09SSaqib Khan     for (const auto& intf : activations)
3944c1aec09SSaqib Khan     {
3954c1aec09SSaqib Khan         if (intf.second->redundancyPriority)
3964c1aec09SSaqib Khan         {
397b77551cdSAdriana Kobylak             priorityMap.insert(std::make_pair(
3982285fe0fSAdriana Kobylak                 intf.first, intf.second->redundancyPriority.get()->priority()));
399b77551cdSAdriana Kobylak         }
400b77551cdSAdriana Kobylak     }
401b77551cdSAdriana Kobylak 
402b77551cdSAdriana Kobylak     // Lambda function to compare 2 priority values, use <= to allow duplicates
4032285fe0fSAdriana Kobylak     typedef std::function<bool(std::pair<std::string, uint8_t>,
4042285fe0fSAdriana Kobylak                                std::pair<std::string, uint8_t>)>
4052285fe0fSAdriana Kobylak         cmpPriority;
4062285fe0fSAdriana Kobylak     cmpPriority cmpPriorityFunc =
4072285fe0fSAdriana Kobylak         [](std::pair<std::string, uint8_t> priority1,
4082285fe0fSAdriana Kobylak            std::pair<std::string, uint8_t> priority2) {
409b77551cdSAdriana Kobylak             return priority1.second <= priority2.second;
410b77551cdSAdriana Kobylak         };
411b77551cdSAdriana Kobylak 
412b77551cdSAdriana Kobylak     // Sort versions by ascending priority
413b77551cdSAdriana Kobylak     std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet(
414b77551cdSAdriana Kobylak         priorityMap.begin(), priorityMap.end(), cmpPriorityFunc);
415b77551cdSAdriana Kobylak 
416b77551cdSAdriana Kobylak     auto freePriorityValue = value;
417b77551cdSAdriana Kobylak     for (auto& element : prioritySet)
418b77551cdSAdriana Kobylak     {
419b77551cdSAdriana Kobylak         if (element.first == versionId)
420b77551cdSAdriana Kobylak         {
421b77551cdSAdriana Kobylak             continue;
422b77551cdSAdriana Kobylak         }
423b77551cdSAdriana Kobylak         if (element.second == freePriorityValue)
424b77551cdSAdriana Kobylak         {
425b77551cdSAdriana Kobylak             ++freePriorityValue;
426b77551cdSAdriana Kobylak             auto it = activations.find(element.first);
427b77551cdSAdriana Kobylak             it->second->redundancyPriority.get()->sdbusPriority(
428b77551cdSAdriana Kobylak                 freePriorityValue);
4294c1aec09SSaqib Khan         }
4304c1aec09SSaqib Khan     }
431b77551cdSAdriana Kobylak 
432b77551cdSAdriana Kobylak     auto lowestVersion = prioritySet.begin()->first;
433b77551cdSAdriana Kobylak     if (value == prioritySet.begin()->second)
434b77551cdSAdriana Kobylak     {
435b77551cdSAdriana Kobylak         lowestVersion = versionId;
4364c1aec09SSaqib Khan     }
437b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestVersion);
4384c1aec09SSaqib Khan }
4394c1aec09SSaqib Khan 
44037a59043SMichael Tritz void ItemUpdater::reset()
44137a59043SMichael Tritz {
44256aaf454SLei YU     helper.factoryReset();
44337a59043SMichael Tritz 
44437a59043SMichael Tritz     log<level::INFO>("BMC factory reset will take effect upon reboot.");
44537a59043SMichael Tritz }
44637a59043SMichael Tritz 
4473526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId)
4483526ef73SLeonel Gonzalez {
44956aaf454SLei YU     helper.removeVersion(versionId);
4503526ef73SLeonel Gonzalez }
4513526ef73SLeonel Gonzalez 
4520129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value)
4530129d926SMichael Tritz {
4540129d926SMichael Tritz     // enabling field mode is intended to be one way: false -> true
4550129d926SMichael Tritz     if (value && !control::FieldMode::fieldModeEnabled())
4560129d926SMichael Tritz     {
4570129d926SMichael Tritz         control::FieldMode::fieldModeEnabled(value);
4580129d926SMichael Tritz 
45956aaf454SLei YU         helper.enableFieldMode();
4600129d926SMichael Tritz     }
4610129d926SMichael Tritz 
4620129d926SMichael Tritz     return control::FieldMode::fieldModeEnabled();
4630129d926SMichael Tritz }
4640129d926SMichael Tritz 
4650129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus()
4660129d926SMichael Tritz {
467ff0b421dSMichael Tritz     std::ifstream input("/dev/mtd/u-boot-env");
4680129d926SMichael Tritz     std::string envVar;
4690129d926SMichael Tritz     std::getline(input, envVar);
4700129d926SMichael Tritz 
4710129d926SMichael Tritz     if (envVar.find("fieldmode=true") != std::string::npos)
4720129d926SMichael Tritz     {
4730129d926SMichael Tritz         ItemUpdater::fieldModeEnabled(true);
4740129d926SMichael Tritz     }
4750129d926SMichael Tritz }
4760129d926SMichael Tritz 
477b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath()
478b60add1eSGunnar Mills {
479b60add1eSGunnar Mills     auto depth = 0;
4802285fe0fSAdriana Kobylak     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
4812285fe0fSAdriana Kobylak                                           MAPPER_INTERFACE, "GetSubTreePaths");
482b60add1eSGunnar Mills 
4831254c628SAdriana Kobylak     mapperCall.append(INVENTORY_PATH);
484b60add1eSGunnar Mills     mapperCall.append(depth);
4851254c628SAdriana Kobylak     std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE};
486b60add1eSGunnar Mills     mapperCall.append(filter);
487b60add1eSGunnar Mills 
48887c78173SEd Tanous     try
489b60add1eSGunnar Mills     {
49087c78173SEd Tanous         auto response = bus.call(mapperCall);
491b60add1eSGunnar Mills 
492b60add1eSGunnar Mills         using ObjectPaths = std::vector<std::string>;
493b60add1eSGunnar Mills         ObjectPaths result;
494b60add1eSGunnar Mills         response.read(result);
495b60add1eSGunnar Mills 
4961254c628SAdriana Kobylak         if (!result.empty())
497b60add1eSGunnar Mills         {
4981254c628SAdriana Kobylak             bmcInventoryPath = result.front();
499b60add1eSGunnar Mills         }
50087c78173SEd Tanous     }
50187c78173SEd Tanous     catch (const sdbusplus::exception::SdBusError& e)
50287c78173SEd Tanous     {
50387c78173SEd Tanous         log<level::ERR>("Error in mapper GetSubTreePath");
50487c78173SEd Tanous         return;
50587c78173SEd Tanous     }
506b60add1eSGunnar Mills 
507b60add1eSGunnar Mills     return;
508b60add1eSGunnar Mills }
509b60add1eSGunnar Mills 
510f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path)
511ded875dcSGunnar Mills {
5122285fe0fSAdriana Kobylak     assocs.emplace_back(
5132285fe0fSAdriana Kobylak         std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
514ded875dcSGunnar Mills     associations(assocs);
515ded875dcSGunnar Mills }
516ded875dcSGunnar Mills 
51788e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path)
51888e8a325SGunnar Mills {
51988e8a325SGunnar Mills     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
5202285fe0fSAdriana Kobylak                                         FUNCTIONAL_REV_ASSOCIATION, path));
52188e8a325SGunnar Mills     associations(assocs);
52288e8a325SGunnar Mills }
52388e8a325SGunnar Mills 
524*991af7ecSAdriana Kobylak void ItemUpdater::removeAssociations(const std::string& path)
525ded875dcSGunnar Mills {
526ded875dcSGunnar Mills     for (auto iter = assocs.begin(); iter != assocs.end();)
527ded875dcSGunnar Mills     {
528*991af7ecSAdriana Kobylak         if ((std::get<2>(*iter)).compare(path) == 0)
529ded875dcSGunnar Mills         {
530ded875dcSGunnar Mills             iter = assocs.erase(iter);
531ded875dcSGunnar Mills             associations(assocs);
532ded875dcSGunnar Mills         }
533ded875dcSGunnar Mills         else
534ded875dcSGunnar Mills         {
535ded875dcSGunnar Mills             ++iter;
536ded875dcSGunnar Mills         }
537ded875dcSGunnar Mills     }
538ded875dcSGunnar Mills }
539ded875dcSGunnar Mills 
540b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value)
541b9da6634SSaqib Khan {
542b9da6634SSaqib Khan     for (const auto& intf : activations)
543b9da6634SSaqib Khan     {
544b9da6634SSaqib Khan         if (intf.second->redundancyPriority)
545b9da6634SSaqib Khan         {
546b9da6634SSaqib Khan             if (intf.second->redundancyPriority.get()->priority() < value)
547b9da6634SSaqib Khan             {
548b9da6634SSaqib Khan                 return false;
549b9da6634SSaqib Khan             }
550b9da6634SSaqib Khan         }
551b9da6634SSaqib Khan     }
552b9da6634SSaqib Khan     return true;
553b9da6634SSaqib Khan }
554b9da6634SSaqib Khan 
555b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId)
556b77551cdSAdriana Kobylak {
55756aaf454SLei YU     helper.updateUbootVersionId(versionId);
558b77551cdSAdriana Kobylak }
559b77551cdSAdriana Kobylak 
56049446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars()
56149446ae9SSaqib Khan {
56249446ae9SSaqib Khan     decltype(activations.begin()->second->redundancyPriority.get()->priority())
56349446ae9SSaqib Khan         lowestPriority = std::numeric_limits<uint8_t>::max();
56449446ae9SSaqib Khan     decltype(activations.begin()->second->versionId) lowestPriorityVersion;
56549446ae9SSaqib Khan     for (const auto& intf : activations)
56649446ae9SSaqib Khan     {
56749446ae9SSaqib Khan         if (!intf.second->redundancyPriority.get())
56849446ae9SSaqib Khan         {
56949446ae9SSaqib Khan             // Skip this version if the redundancyPriority is not initialized.
57049446ae9SSaqib Khan             continue;
57149446ae9SSaqib Khan         }
57249446ae9SSaqib Khan 
5732285fe0fSAdriana Kobylak         if (intf.second->redundancyPriority.get()->priority() <= lowestPriority)
57449446ae9SSaqib Khan         {
57549446ae9SSaqib Khan             lowestPriority = intf.second->redundancyPriority.get()->priority();
57649446ae9SSaqib Khan             lowestPriorityVersion = intf.second->versionId;
57749446ae9SSaqib Khan         }
57849446ae9SSaqib Khan     }
57949446ae9SSaqib Khan 
580f0382c35SSaqib Khan     // Update the U-boot environment variable to point to the lowest priority
581b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestPriorityVersion);
58249446ae9SSaqib Khan }
58349446ae9SSaqib Khan 
584a6963590SAdriana Kobylak void ItemUpdater::freeSpace(Activation& caller)
585204e1e74SAdriana Kobylak {
586204e1e74SAdriana Kobylak     //  Versions with the highest priority in front
587204e1e74SAdriana Kobylak     std::priority_queue<std::pair<int, std::string>,
588204e1e74SAdriana Kobylak                         std::vector<std::pair<int, std::string>>,
5892285fe0fSAdriana Kobylak                         std::less<std::pair<int, std::string>>>
5902285fe0fSAdriana Kobylak         versionsPQ;
591204e1e74SAdriana Kobylak 
592204e1e74SAdriana Kobylak     std::size_t count = 0;
593204e1e74SAdriana Kobylak     for (const auto& iter : activations)
594204e1e74SAdriana Kobylak     {
595204e1e74SAdriana Kobylak         if ((iter.second.get()->activation() ==
596204e1e74SAdriana Kobylak              server::Activation::Activations::Active) ||
597204e1e74SAdriana Kobylak             (iter.second.get()->activation() ==
598204e1e74SAdriana Kobylak              server::Activation::Activations::Failed))
599204e1e74SAdriana Kobylak         {
600204e1e74SAdriana Kobylak             count++;
601204e1e74SAdriana Kobylak             // Don't put the functional version on the queue since we can't
602204e1e74SAdriana Kobylak             // remove the "running" BMC version.
6030f88b5afSLei YU             // If ACTIVE_BMC_MAX_ALLOWED <= 1, there is only one active BMC,
6040f88b5afSLei YU             // so remove functional version as well.
605a6963590SAdriana Kobylak             // Don't delete the the Activation object that called this function.
606a6963590SAdriana Kobylak             if ((versions.find(iter.second->versionId)
607a6963590SAdriana Kobylak                      ->second->isFunctional() &&
608a6963590SAdriana Kobylak                  ACTIVE_BMC_MAX_ALLOWED > 1) ||
609a6963590SAdriana Kobylak                 (iter.second->versionId == caller.versionId))
610204e1e74SAdriana Kobylak             {
611204e1e74SAdriana Kobylak                 continue;
612204e1e74SAdriana Kobylak             }
613a6963590SAdriana Kobylak 
614a6963590SAdriana Kobylak             // Failed activations don't have priority, assign them a large value
615a6963590SAdriana Kobylak             // for sorting purposes.
616a6963590SAdriana Kobylak             auto priority = 999;
617a6963590SAdriana Kobylak             if (iter.second.get()->activation() ==
618a6963590SAdriana Kobylak                 server::Activation::Activations::Active)
619a6963590SAdriana Kobylak             {
620a6963590SAdriana Kobylak                 priority = iter.second->redundancyPriority.get()->priority();
621a6963590SAdriana Kobylak             }
622a6963590SAdriana Kobylak 
623a6963590SAdriana Kobylak             versionsPQ.push(std::make_pair(priority, iter.second->versionId));
624204e1e74SAdriana Kobylak         }
625204e1e74SAdriana Kobylak     }
626204e1e74SAdriana Kobylak 
627204e1e74SAdriana Kobylak     // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1,
628204e1e74SAdriana Kobylak     // remove the highest priority one(s).
629204e1e74SAdriana Kobylak     while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty()))
630204e1e74SAdriana Kobylak     {
631204e1e74SAdriana Kobylak         erase(versionsPQ.top().second);
632204e1e74SAdriana Kobylak         versionsPQ.pop();
633204e1e74SAdriana Kobylak         count--;
634204e1e74SAdriana Kobylak     }
635204e1e74SAdriana Kobylak }
636204e1e74SAdriana Kobylak 
637eaa1ee05SEddie James void ItemUpdater::mirrorUbootToAlt()
638eaa1ee05SEddie James {
63956aaf454SLei YU     helper.mirrorAlt();
640eaa1ee05SEddie James }
641eaa1ee05SEddie James 
642ec1b41c4SGunnar Mills } // namespace updater
643ec1b41c4SGunnar Mills } // namespace software
644ec1b41c4SGunnar Mills } // namespace phosphor
645