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 <experimental/filesystem>
1135e83f3eSSaqib Khan #include <fstream>
12d5b8f75cSAdriana Kobylak #include <phosphor-logging/elog-errors.hpp>
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>
18d5b8f75cSAdriana Kobylak #include <xyz/openbmc_project/Common/error.hpp>
1943699ca7SAdriana Kobylak #include <xyz/openbmc_project/Software/Image/error.hpp>
20ec1b41c4SGunnar Mills 
21ec1b41c4SGunnar Mills namespace phosphor
22ec1b41c4SGunnar Mills {
23ec1b41c4SGunnar Mills namespace software
24ec1b41c4SGunnar Mills {
25ec1b41c4SGunnar Mills namespace updater
26ec1b41c4SGunnar Mills {
27ec1b41c4SGunnar Mills 
282ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class
292ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server;
300129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server;
312ce7da29SGunnar Mills 
322ce7da29SGunnar Mills using namespace phosphor::logging;
3343699ca7SAdriana Kobylak using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error;
342ab9b109SJayanth Othayoth using namespace phosphor::software::image;
3535e83f3eSSaqib Khan namespace fs = std::experimental::filesystem;
36d5b8f75cSAdriana Kobylak using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
3735e83f3eSSaqib Khan 
38e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg)
39ec1b41c4SGunnar Mills {
4084a0e693SSaqib Khan 
4184a0e693SSaqib Khan     using SVersion = server::Version;
4284a0e693SSaqib Khan     using VersionPurpose = SVersion::VersionPurpose;
439a782243SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
4484a0e693SSaqib Khan     namespace mesg = sdbusplus::message;
4584a0e693SSaqib Khan     namespace variant_ns = mesg::variant_ns;
4684a0e693SSaqib Khan 
4784a0e693SSaqib Khan     mesg::object_path objPath;
4884a0e693SSaqib Khan     auto purpose = VersionPurpose::Unknown;
49705f1bfcSSaqib Khan     std::string version;
502285fe0fSAdriana Kobylak     std::map<std::string, std::map<std::string, mesg::variant<std::string>>>
512285fe0fSAdriana Kobylak         interfaces;
52e75d10f5SPatrick Williams     msg.read(objPath, interfaces);
532ce7da29SGunnar Mills     std::string path(std::move(objPath));
5419177d3eSSaqib Khan     std::string filePath;
552ce7da29SGunnar Mills 
562ce7da29SGunnar Mills     for (const auto& intf : interfaces)
572ce7da29SGunnar Mills     {
58705f1bfcSSaqib Khan         if (intf.first == VERSION_IFACE)
592ce7da29SGunnar Mills         {
602ce7da29SGunnar Mills             for (const auto& property : intf.second)
612ce7da29SGunnar Mills             {
62705f1bfcSSaqib Khan                 if (property.first == "Purpose")
632ce7da29SGunnar Mills                 {
6484a0e693SSaqib Khan                     auto value = SVersion::convertVersionPurposeFromString(
6584a0e693SSaqib Khan                         variant_ns::get<std::string>(property.second));
6684a0e693SSaqib Khan                     if (value == VersionPurpose::BMC ||
6784a0e693SSaqib Khan                         value == VersionPurpose::System)
6884a0e693SSaqib Khan                     {
6984a0e693SSaqib Khan                         purpose = value;
7084a0e693SSaqib Khan                     }
71705f1bfcSSaqib Khan                 }
72705f1bfcSSaqib Khan                 else if (property.first == "Version")
73705f1bfcSSaqib Khan                 {
7484a0e693SSaqib Khan                     version = variant_ns::get<std::string>(property.second);
75705f1bfcSSaqib Khan                 }
76705f1bfcSSaqib Khan             }
77705f1bfcSSaqib Khan         }
7819177d3eSSaqib Khan         else if (intf.first == FILEPATH_IFACE)
7919177d3eSSaqib Khan         {
8019177d3eSSaqib Khan             for (const auto& property : intf.second)
8119177d3eSSaqib Khan             {
8219177d3eSSaqib Khan                 if (property.first == "Path")
8319177d3eSSaqib Khan                 {
8484a0e693SSaqib Khan                     filePath = variant_ns::get<std::string>(property.second);
8519177d3eSSaqib Khan                 }
8619177d3eSSaqib Khan             }
8719177d3eSSaqib Khan         }
88705f1bfcSSaqib Khan     }
892285fe0fSAdriana Kobylak     if (version.empty() || filePath.empty() ||
9084a0e693SSaqib Khan         purpose == VersionPurpose::Unknown)
912ce7da29SGunnar Mills     {
92e75d10f5SPatrick Williams         return;
932ce7da29SGunnar Mills     }
942ce7da29SGunnar Mills 
952ce7da29SGunnar Mills     // Version id is the last item in the path
962ce7da29SGunnar Mills     auto pos = path.rfind("/");
972ce7da29SGunnar Mills     if (pos == std::string::npos)
982ce7da29SGunnar Mills     {
992ce7da29SGunnar Mills         log<level::ERR>("No version id found in object path",
100596466b8SAdriana Kobylak                         entry("OBJPATH=%s", path.c_str()));
101e75d10f5SPatrick Williams         return;
1022ce7da29SGunnar Mills     }
1032ce7da29SGunnar Mills 
1042ce7da29SGunnar Mills     auto versionId = path.substr(pos + 1);
1052ce7da29SGunnar Mills 
106e75d10f5SPatrick Williams     if (activations.find(versionId) == activations.end())
1072ce7da29SGunnar Mills     {
10835e83f3eSSaqib Khan         // Determine the Activation state by processing the given image dir.
10935e83f3eSSaqib Khan         auto activationState = server::Activation::Activations::Invalid;
1109a782243SGunnar Mills         ItemUpdater::ActivationStatus result =
1119a782243SGunnar Mills             ItemUpdater::validateSquashFSImage(filePath);
11243b25cdeSGunnar Mills         AssociationList associations = {};
11343b25cdeSGunnar Mills 
11435e83f3eSSaqib Khan         if (result == ItemUpdater::ActivationStatus::ready)
11535e83f3eSSaqib Khan         {
11635e83f3eSSaqib Khan             activationState = server::Activation::Activations::Ready;
117b60add1eSGunnar Mills             // Create an association to the BMC inventory item
1182285fe0fSAdriana Kobylak             associations.emplace_back(
1192285fe0fSAdriana Kobylak                 std::make_tuple(ACTIVATION_FWD_ASSOCIATION,
1202285fe0fSAdriana Kobylak                                 ACTIVATION_REV_ASSOCIATION, bmcInventoryPath));
12143b25cdeSGunnar Mills         }
122b60add1eSGunnar Mills 
123ee13e831SSaqib Khan         activations.insert(std::make_pair(
124ee13e831SSaqib Khan             versionId,
1252285fe0fSAdriana Kobylak             std::make_unique<Activation>(bus, path, *this, versionId,
1262285fe0fSAdriana Kobylak                                          activationState, associations)));
1274254beceSMichael Tritz 
128ee13e831SSaqib Khan         auto versionPtr = std::make_unique<VersionClass>(
1292285fe0fSAdriana Kobylak             bus, path, version, purpose, filePath,
1302285fe0fSAdriana Kobylak             std::bind(&ItemUpdater::erase, this, std::placeholders::_1));
131ee13e831SSaqib Khan         versionPtr->deleteObject =
1322285fe0fSAdriana Kobylak             std::make_unique<phosphor::software::manager::Delete>(bus, path,
1332285fe0fSAdriana Kobylak                                                                   *versionPtr);
134ee13e831SSaqib Khan         versions.insert(std::make_pair(versionId, std::move(versionPtr)));
1352ce7da29SGunnar Mills     }
136e75d10f5SPatrick Williams     return;
137ec1b41c4SGunnar Mills }
138ec1b41c4SGunnar Mills 
139ba239881SSaqib Khan void ItemUpdater::processBMCImage()
140ba239881SSaqib Khan {
14188e8a325SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
142269bff30SLei YU 
143269bff30SLei YU     // Check MEDIA_DIR and create if it does not exist
144269bff30SLei YU     try
145269bff30SLei YU     {
146269bff30SLei YU         if (!fs::is_directory(MEDIA_DIR))
147269bff30SLei YU         {
148269bff30SLei YU             fs::create_directory(MEDIA_DIR);
149269bff30SLei YU         }
150269bff30SLei YU     }
151269bff30SLei YU     catch (const fs::filesystem_error& e)
152269bff30SLei YU     {
153269bff30SLei YU         log<level::ERR>("Failed to prepare dir", entry("ERR=%s", e.what()));
154269bff30SLei YU         return;
155269bff30SLei YU     }
156269bff30SLei YU 
15788e8a325SGunnar Mills     // Read os-release from /etc/ to get the functional BMC version
15888e8a325SGunnar Mills     auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE);
15988e8a325SGunnar Mills 
1601eef62deSSaqib Khan     // Read os-release from folders under /media/ to get
1611eef62deSSaqib Khan     // BMC Software Versions.
1621eef62deSSaqib Khan     for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
1631eef62deSSaqib Khan     {
1641eef62deSSaqib Khan         auto activationState = server::Activation::Activations::Active;
1656fab70daSSaqib Khan         static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX);
1661eef62deSSaqib Khan 
1671eef62deSSaqib Khan         // Check if the BMC_RO_PREFIXis the prefix of the iter.path
1682285fe0fSAdriana Kobylak         if (0 ==
1692285fe0fSAdriana Kobylak             iter.path().native().compare(0, BMC_RO_PREFIX_LEN, BMC_ROFS_PREFIX))
1701eef62deSSaqib Khan         {
171021c365bSSaqib Khan             // The versionId is extracted from the path
172021c365bSSaqib Khan             // for example /media/ro-2a1022fe.
173021c365bSSaqib Khan             auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
1741eef62deSSaqib Khan             auto osRelease = iter.path() / OS_RELEASE_FILE;
1751eef62deSSaqib Khan             if (!fs::is_regular_file(osRelease))
1761eef62deSSaqib Khan             {
1772285fe0fSAdriana Kobylak                 log<level::ERR>(
1782285fe0fSAdriana Kobylak                     "Failed to read osRelease",
179596466b8SAdriana Kobylak                     entry("FILENAME=%s", osRelease.string().c_str()));
180021c365bSSaqib Khan                 ItemUpdater::erase(id);
181021c365bSSaqib Khan                 continue;
1821eef62deSSaqib Khan             }
18388e8a325SGunnar Mills             auto version = VersionClass::getBMCVersion(osRelease);
1841eef62deSSaqib Khan             if (version.empty())
1851eef62deSSaqib Khan             {
1862285fe0fSAdriana Kobylak                 log<level::ERR>(
1872285fe0fSAdriana Kobylak                     "Failed to read version from osRelease",
188596466b8SAdriana Kobylak                     entry("FILENAME=%s", osRelease.string().c_str()));
1891eef62deSSaqib Khan                 activationState = server::Activation::Activations::Invalid;
1901eef62deSSaqib Khan             }
191021c365bSSaqib Khan 
1921eef62deSSaqib Khan             auto purpose = server::Version::VersionPurpose::BMC;
1931eef62deSSaqib Khan             auto path = fs::path(SOFTWARE_OBJPATH) / id;
1941eef62deSSaqib Khan 
195269bff30SLei YU             // Create functional association if this is the functional
196269bff30SLei YU             // version
19788e8a325SGunnar Mills             if (version.compare(functionalVersion) == 0)
19888e8a325SGunnar Mills             {
19988e8a325SGunnar Mills                 createFunctionalAssociation(path);
20088e8a325SGunnar Mills             }
20188e8a325SGunnar Mills 
20243b25cdeSGunnar Mills             AssociationList associations = {};
20343b25cdeSGunnar Mills 
20443b25cdeSGunnar Mills             if (activationState == server::Activation::Activations::Active)
20543b25cdeSGunnar Mills             {
20643b25cdeSGunnar Mills                 // Create an association to the BMC inventory item
20743b25cdeSGunnar Mills                 associations.emplace_back(std::make_tuple(
2082285fe0fSAdriana Kobylak                     ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
20943b25cdeSGunnar Mills                     bmcInventoryPath));
21043b25cdeSGunnar Mills 
21143b25cdeSGunnar Mills                 // Create an active association since this image is active
21243b25cdeSGunnar Mills                 createActiveAssociation(path);
21343b25cdeSGunnar Mills             }
21443b25cdeSGunnar Mills 
215ee590c74SAdriana Kobylak             // Create Version instance for this version.
216ee590c74SAdriana Kobylak             auto versionPtr = std::make_unique<VersionClass>(
2172285fe0fSAdriana Kobylak                 bus, path, version, purpose, "",
2182285fe0fSAdriana Kobylak                 std::bind(&ItemUpdater::erase, this, std::placeholders::_1));
219ee590c74SAdriana Kobylak             auto isVersionFunctional = versionPtr->isFunctional();
220ee13e831SSaqib Khan             if (!isVersionFunctional)
221ee13e831SSaqib Khan             {
222ee13e831SSaqib Khan                 versionPtr->deleteObject =
223ee13e831SSaqib Khan                     std::make_unique<phosphor::software::manager::Delete>(
224ee13e831SSaqib Khan                         bus, path, *versionPtr);
225ee13e831SSaqib Khan             }
2262285fe0fSAdriana Kobylak             versions.insert(std::make_pair(id, std::move(versionPtr)));
227ee590c74SAdriana Kobylak 
2281eef62deSSaqib Khan             // Create Activation instance for this version.
229ee13e831SSaqib Khan             activations.insert(std::make_pair(
2302285fe0fSAdriana Kobylak                 id, std::make_unique<Activation>(
2312285fe0fSAdriana Kobylak                         bus, path, *this, id, activationState, associations)));
2321eef62deSSaqib Khan 
233269bff30SLei YU             // If Active, create RedundancyPriority instance for this
234269bff30SLei YU             // version.
2351eef62deSSaqib Khan             if (activationState == server::Activation::Activations::Active)
2361eef62deSSaqib Khan             {
2371eef62deSSaqib Khan                 uint8_t priority = std::numeric_limits<uint8_t>::max();
238*687e75e2SAdriana Kobylak                 if (!restorePriority(id, priority))
2391eef62deSSaqib Khan                 {
240ee590c74SAdriana Kobylak                     if (isVersionFunctional)
241ee590c74SAdriana Kobylak                     {
242ee590c74SAdriana Kobylak                         priority = 0;
243ee590c74SAdriana Kobylak                     }
244ee590c74SAdriana Kobylak                     else
245ee590c74SAdriana Kobylak                     {
2461eef62deSSaqib Khan                         log<level::ERR>("Unable to restore priority from file.",
247596466b8SAdriana Kobylak                                         entry("VERSIONID=%s", id.c_str()));
2481eef62deSSaqib Khan                     }
249ee590c74SAdriana Kobylak                 }
2501eef62deSSaqib Khan                 activations.find(id)->second->redundancyPriority =
2511eef62deSSaqib Khan                     std::make_unique<RedundancyPriority>(
2522285fe0fSAdriana Kobylak                         bus, path, *(activations.find(id)->second), priority,
253b77551cdSAdriana Kobylak                         false);
2541eef62deSSaqib Khan             }
2551eef62deSSaqib Khan         }
2561eef62deSSaqib Khan     }
257dcbfa04aSSaqib Khan 
258dcbfa04aSSaqib Khan     // If there is no ubi volume for bmc version then read the /etc/os-release
259dcbfa04aSSaqib Khan     // and create rofs-<versionId> under /media
260dcbfa04aSSaqib Khan     if (activations.size() == 0)
261dcbfa04aSSaqib Khan     {
262d16bcbd5SGunnar Mills         auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE);
263dcbfa04aSSaqib Khan         auto id = phosphor::software::manager::Version::getId(version);
264dcbfa04aSSaqib Khan         auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/";
265dcbfa04aSSaqib Khan         try
266dcbfa04aSSaqib Khan         {
267dcbfa04aSSaqib Khan             if (!fs::is_directory(versionFileDir))
268dcbfa04aSSaqib Khan             {
269dcbfa04aSSaqib Khan                 fs::create_directories(versionFileDir);
270dcbfa04aSSaqib Khan             }
271dcbfa04aSSaqib Khan             auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE;
272dcbfa04aSSaqib Khan             fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath);
273dcbfa04aSSaqib Khan             ItemUpdater::processBMCImage();
274dcbfa04aSSaqib Khan         }
275dcbfa04aSSaqib Khan         catch (const std::exception& e)
276dcbfa04aSSaqib Khan         {
277dcbfa04aSSaqib Khan             log<level::ERR>(e.what());
278dcbfa04aSSaqib Khan         }
279dcbfa04aSSaqib Khan     }
280eaa1ee05SEddie James 
281eaa1ee05SEddie James     mirrorUbootToAlt();
282ba239881SSaqib Khan     return;
283ba239881SSaqib Khan }
284ba239881SSaqib Khan 
2853526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId)
2863526ef73SLeonel Gonzalez {
2876d873715SEddie James     // Find entry in versions map
2886d873715SEddie James     auto it = versions.find(entryId);
2896d873715SEddie James     if (it != versions.end())
2906d873715SEddie James     {
2910f88b5afSLei YU         if (it->second->isFunctional() && ACTIVE_BMC_MAX_ALLOWED > 1)
2926d873715SEddie James         {
2932285fe0fSAdriana Kobylak             log<level::ERR>("Error: Version is currently running on the BMC. "
2942285fe0fSAdriana Kobylak                             "Unable to remove.",
2952285fe0fSAdriana Kobylak                             entry("VERSIONID=%s", entryId.c_str()));
2966d873715SEddie James             return;
2976d873715SEddie James         }
2986d873715SEddie James 
2996d873715SEddie James         // Delete ReadOnly partitions if it's not active
3003526ef73SLeonel Gonzalez         removeReadOnlyPartition(entryId);
301*687e75e2SAdriana Kobylak         removePersistDataDirectory(entryId);
302ee13e831SSaqib Khan 
303ee13e831SSaqib Khan         // Removing entry in versions map
304ee13e831SSaqib Khan         this->versions.erase(entryId);
3056d873715SEddie James     }
3066d873715SEddie James     else
3076d873715SEddie James     {
3086d873715SEddie James         // Delete ReadOnly partitions even if we can't find the version
3096d873715SEddie James         removeReadOnlyPartition(entryId);
310*687e75e2SAdriana Kobylak         removePersistDataDirectory(entryId);
3116d873715SEddie James 
3122285fe0fSAdriana Kobylak         log<level::ERR>("Error: Failed to find version in item updater "
3132285fe0fSAdriana Kobylak                         "versions map. Unable to remove.",
3142285fe0fSAdriana Kobylak                         entry("VERSIONID=%s", entryId.c_str()));
3156d873715SEddie James     }
3161eef62deSSaqib Khan 
31756aaf454SLei YU     helper.clearEntry(entryId);
3183526ef73SLeonel Gonzalez 
3193526ef73SLeonel Gonzalez     // Removing entry in activations map
3203526ef73SLeonel Gonzalez     auto ita = activations.find(entryId);
3213526ef73SLeonel Gonzalez     if (ita == activations.end())
3223526ef73SLeonel Gonzalez     {
3232285fe0fSAdriana Kobylak         log<level::ERR>("Error: Failed to find version in item updater "
3242285fe0fSAdriana Kobylak                         "activations map. Unable to remove.",
3252285fe0fSAdriana Kobylak                         entry("VERSIONID=%s", entryId.c_str()));
3263526ef73SLeonel Gonzalez     }
327ee13e831SSaqib Khan     else
328ee13e831SSaqib Khan     {
329991af7ecSAdriana Kobylak         removeAssociations(ita->second->path);
3303526ef73SLeonel Gonzalez         this->activations.erase(entryId);
331ee13e831SSaqib Khan     }
33249446ae9SSaqib Khan     ItemUpdater::resetUbootEnvVars();
333ee13e831SSaqib Khan     return;
3343526ef73SLeonel Gonzalez }
3353526ef73SLeonel Gonzalez 
336bc1bf3afSMichael Tritz void ItemUpdater::deleteAll()
337bc1bf3afSMichael Tritz {
33883cd21fbSAdriana Kobylak     std::vector<std::string> deletableVersions;
33983cd21fbSAdriana Kobylak 
340bc1bf3afSMichael Tritz     for (const auto& versionIt : versions)
341bc1bf3afSMichael Tritz     {
342bc1bf3afSMichael Tritz         if (!versionIt.second->isFunctional())
343bc1bf3afSMichael Tritz         {
34483cd21fbSAdriana Kobylak             deletableVersions.push_back(versionIt.first);
345bc1bf3afSMichael Tritz         }
346bc1bf3afSMichael Tritz     }
347bc1bf3afSMichael Tritz 
34883cd21fbSAdriana Kobylak     for (const auto& deletableIt : deletableVersions)
34983cd21fbSAdriana Kobylak     {
35083cd21fbSAdriana Kobylak         ItemUpdater::erase(deletableIt);
35183cd21fbSAdriana Kobylak     }
35283cd21fbSAdriana Kobylak 
35356aaf454SLei YU     helper.cleanup();
354bc1bf3afSMichael Tritz }
355bc1bf3afSMichael Tritz 
3562285fe0fSAdriana Kobylak ItemUpdater::ActivationStatus
3572285fe0fSAdriana Kobylak     ItemUpdater::validateSquashFSImage(const std::string& filePath)
35835e83f3eSSaqib Khan {
359b1cfdf99SMichael Tritz     bool invalid = false;
36035e83f3eSSaqib Khan 
361b1cfdf99SMichael Tritz     for (auto& bmcImage : bmcImages)
362b1cfdf99SMichael Tritz     {
36319177d3eSSaqib Khan         fs::path file(filePath);
36435e83f3eSSaqib Khan         file /= bmcImage;
36535e83f3eSSaqib Khan         std::ifstream efile(file.c_str());
366b1cfdf99SMichael Tritz         if (efile.good() != 1)
36735e83f3eSSaqib Khan         {
368b1cfdf99SMichael Tritz             log<level::ERR>("Failed to find the BMC image.",
369b1cfdf99SMichael Tritz                             entry("IMAGE=%s", bmcImage.c_str()));
370b1cfdf99SMichael Tritz             invalid = true;
37135e83f3eSSaqib Khan         }
372b1cfdf99SMichael Tritz     }
373b1cfdf99SMichael Tritz 
374b1cfdf99SMichael Tritz     if (invalid)
37535e83f3eSSaqib Khan     {
37635e83f3eSSaqib Khan         return ItemUpdater::ActivationStatus::invalid;
37735e83f3eSSaqib Khan     }
378b1cfdf99SMichael Tritz 
379b1cfdf99SMichael Tritz     return ItemUpdater::ActivationStatus::ready;
38035e83f3eSSaqib Khan }
38135e83f3eSSaqib Khan 
382bbcb7be1SAdriana Kobylak void ItemUpdater::savePriority(const std::string& versionId, uint8_t value)
383bbcb7be1SAdriana Kobylak {
384*687e75e2SAdriana Kobylak     storePriority(versionId, value);
385bbcb7be1SAdriana Kobylak     helper.setEntry(versionId, value);
386bbcb7be1SAdriana Kobylak }
387bbcb7be1SAdriana Kobylak 
388b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
3894c1aec09SSaqib Khan {
390b77551cdSAdriana Kobylak     std::map<std::string, uint8_t> priorityMap;
391b77551cdSAdriana Kobylak 
392b77551cdSAdriana Kobylak     // Insert the requested version and priority, it may not exist yet.
393b77551cdSAdriana Kobylak     priorityMap.insert(std::make_pair(versionId, value));
394b77551cdSAdriana Kobylak 
3954c1aec09SSaqib Khan     for (const auto& intf : activations)
3964c1aec09SSaqib Khan     {
3974c1aec09SSaqib Khan         if (intf.second->redundancyPriority)
3984c1aec09SSaqib Khan         {
399b77551cdSAdriana Kobylak             priorityMap.insert(std::make_pair(
4002285fe0fSAdriana Kobylak                 intf.first, intf.second->redundancyPriority.get()->priority()));
401b77551cdSAdriana Kobylak         }
402b77551cdSAdriana Kobylak     }
403b77551cdSAdriana Kobylak 
404b77551cdSAdriana Kobylak     // Lambda function to compare 2 priority values, use <= to allow duplicates
4052285fe0fSAdriana Kobylak     typedef std::function<bool(std::pair<std::string, uint8_t>,
4062285fe0fSAdriana Kobylak                                std::pair<std::string, uint8_t>)>
4072285fe0fSAdriana Kobylak         cmpPriority;
4082285fe0fSAdriana Kobylak     cmpPriority cmpPriorityFunc =
4092285fe0fSAdriana Kobylak         [](std::pair<std::string, uint8_t> priority1,
4102285fe0fSAdriana Kobylak            std::pair<std::string, uint8_t> priority2) {
411b77551cdSAdriana Kobylak             return priority1.second <= priority2.second;
412b77551cdSAdriana Kobylak         };
413b77551cdSAdriana Kobylak 
414b77551cdSAdriana Kobylak     // Sort versions by ascending priority
415b77551cdSAdriana Kobylak     std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet(
416b77551cdSAdriana Kobylak         priorityMap.begin(), priorityMap.end(), cmpPriorityFunc);
417b77551cdSAdriana Kobylak 
418b77551cdSAdriana Kobylak     auto freePriorityValue = value;
419b77551cdSAdriana Kobylak     for (auto& element : prioritySet)
420b77551cdSAdriana Kobylak     {
421b77551cdSAdriana Kobylak         if (element.first == versionId)
422b77551cdSAdriana Kobylak         {
423b77551cdSAdriana Kobylak             continue;
424b77551cdSAdriana Kobylak         }
425b77551cdSAdriana Kobylak         if (element.second == freePriorityValue)
426b77551cdSAdriana Kobylak         {
427b77551cdSAdriana Kobylak             ++freePriorityValue;
428b77551cdSAdriana Kobylak             auto it = activations.find(element.first);
429b77551cdSAdriana Kobylak             it->second->redundancyPriority.get()->sdbusPriority(
430b77551cdSAdriana Kobylak                 freePriorityValue);
4314c1aec09SSaqib Khan         }
4324c1aec09SSaqib Khan     }
433b77551cdSAdriana Kobylak 
434b77551cdSAdriana Kobylak     auto lowestVersion = prioritySet.begin()->first;
435b77551cdSAdriana Kobylak     if (value == prioritySet.begin()->second)
436b77551cdSAdriana Kobylak     {
437b77551cdSAdriana Kobylak         lowestVersion = versionId;
4384c1aec09SSaqib Khan     }
439b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestVersion);
4404c1aec09SSaqib Khan }
4414c1aec09SSaqib Khan 
44237a59043SMichael Tritz void ItemUpdater::reset()
44337a59043SMichael Tritz {
44456aaf454SLei YU     helper.factoryReset();
44537a59043SMichael Tritz 
44637a59043SMichael Tritz     log<level::INFO>("BMC factory reset will take effect upon reboot.");
44737a59043SMichael Tritz }
44837a59043SMichael Tritz 
4493526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId)
4503526ef73SLeonel Gonzalez {
45156aaf454SLei YU     helper.removeVersion(versionId);
4523526ef73SLeonel Gonzalez }
4533526ef73SLeonel Gonzalez 
4540129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value)
4550129d926SMichael Tritz {
4560129d926SMichael Tritz     // enabling field mode is intended to be one way: false -> true
4570129d926SMichael Tritz     if (value && !control::FieldMode::fieldModeEnabled())
4580129d926SMichael Tritz     {
4590129d926SMichael Tritz         control::FieldMode::fieldModeEnabled(value);
4600129d926SMichael Tritz 
46122848eceSAdriana Kobylak         auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
46222848eceSAdriana Kobylak                                           SYSTEMD_INTERFACE, "StartUnit");
46322848eceSAdriana Kobylak         method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service",
46422848eceSAdriana Kobylak                       "replace");
46522848eceSAdriana Kobylak         bus.call_noreply(method);
46622848eceSAdriana Kobylak 
46722848eceSAdriana Kobylak         method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
46822848eceSAdriana Kobylak                                      SYSTEMD_INTERFACE, "StopUnit");
46922848eceSAdriana Kobylak         method.append("usr-local.mount", "replace");
47022848eceSAdriana Kobylak         bus.call_noreply(method);
47122848eceSAdriana Kobylak 
47222848eceSAdriana Kobylak         std::vector<std::string> usrLocal = {"usr-local.mount"};
47322848eceSAdriana Kobylak 
47422848eceSAdriana Kobylak         method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
47522848eceSAdriana Kobylak                                      SYSTEMD_INTERFACE, "MaskUnitFiles");
47622848eceSAdriana Kobylak         method.append(usrLocal, false, true);
47722848eceSAdriana Kobylak         bus.call_noreply(method);
4780129d926SMichael Tritz     }
479d5b8f75cSAdriana Kobylak     else if (!value && control::FieldMode::fieldModeEnabled())
480d5b8f75cSAdriana Kobylak     {
481d5b8f75cSAdriana Kobylak         elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON(
482d5b8f75cSAdriana Kobylak             "FieldMode is not allowed to be cleared"));
483d5b8f75cSAdriana Kobylak     }
4840129d926SMichael Tritz 
4850129d926SMichael Tritz     return control::FieldMode::fieldModeEnabled();
4860129d926SMichael Tritz }
4870129d926SMichael Tritz 
4880129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus()
4890129d926SMichael Tritz {
490ff0b421dSMichael Tritz     std::ifstream input("/dev/mtd/u-boot-env");
4910129d926SMichael Tritz     std::string envVar;
4920129d926SMichael Tritz     std::getline(input, envVar);
4930129d926SMichael Tritz 
4940129d926SMichael Tritz     if (envVar.find("fieldmode=true") != std::string::npos)
4950129d926SMichael Tritz     {
4960129d926SMichael Tritz         ItemUpdater::fieldModeEnabled(true);
4970129d926SMichael Tritz     }
4980129d926SMichael Tritz }
4990129d926SMichael Tritz 
500b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath()
501b60add1eSGunnar Mills {
502b60add1eSGunnar Mills     auto depth = 0;
5032285fe0fSAdriana Kobylak     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
5042285fe0fSAdriana Kobylak                                           MAPPER_INTERFACE, "GetSubTreePaths");
505b60add1eSGunnar Mills 
5061254c628SAdriana Kobylak     mapperCall.append(INVENTORY_PATH);
507b60add1eSGunnar Mills     mapperCall.append(depth);
5081254c628SAdriana Kobylak     std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE};
509b60add1eSGunnar Mills     mapperCall.append(filter);
510b60add1eSGunnar Mills 
51187c78173SEd Tanous     try
512b60add1eSGunnar Mills     {
51387c78173SEd Tanous         auto response = bus.call(mapperCall);
514b60add1eSGunnar Mills 
515b60add1eSGunnar Mills         using ObjectPaths = std::vector<std::string>;
516b60add1eSGunnar Mills         ObjectPaths result;
517b60add1eSGunnar Mills         response.read(result);
518b60add1eSGunnar Mills 
5191254c628SAdriana Kobylak         if (!result.empty())
520b60add1eSGunnar Mills         {
5211254c628SAdriana Kobylak             bmcInventoryPath = result.front();
522b60add1eSGunnar Mills         }
52387c78173SEd Tanous     }
52487c78173SEd Tanous     catch (const sdbusplus::exception::SdBusError& e)
52587c78173SEd Tanous     {
52687c78173SEd Tanous         log<level::ERR>("Error in mapper GetSubTreePath");
52787c78173SEd Tanous         return;
52887c78173SEd Tanous     }
529b60add1eSGunnar Mills 
530b60add1eSGunnar Mills     return;
531b60add1eSGunnar Mills }
532b60add1eSGunnar Mills 
533f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path)
534ded875dcSGunnar Mills {
5352285fe0fSAdriana Kobylak     assocs.emplace_back(
5362285fe0fSAdriana Kobylak         std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
537ded875dcSGunnar Mills     associations(assocs);
538ded875dcSGunnar Mills }
539ded875dcSGunnar Mills 
54088e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path)
54188e8a325SGunnar Mills {
54288e8a325SGunnar Mills     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
5432285fe0fSAdriana Kobylak                                         FUNCTIONAL_REV_ASSOCIATION, path));
54488e8a325SGunnar Mills     associations(assocs);
54588e8a325SGunnar Mills }
54688e8a325SGunnar Mills 
547991af7ecSAdriana Kobylak void ItemUpdater::removeAssociations(const std::string& path)
548ded875dcSGunnar Mills {
549ded875dcSGunnar Mills     for (auto iter = assocs.begin(); iter != assocs.end();)
550ded875dcSGunnar Mills     {
551991af7ecSAdriana Kobylak         if ((std::get<2>(*iter)).compare(path) == 0)
552ded875dcSGunnar Mills         {
553ded875dcSGunnar Mills             iter = assocs.erase(iter);
554ded875dcSGunnar Mills             associations(assocs);
555ded875dcSGunnar Mills         }
556ded875dcSGunnar Mills         else
557ded875dcSGunnar Mills         {
558ded875dcSGunnar Mills             ++iter;
559ded875dcSGunnar Mills         }
560ded875dcSGunnar Mills     }
561ded875dcSGunnar Mills }
562ded875dcSGunnar Mills 
563b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value)
564b9da6634SSaqib Khan {
565b9da6634SSaqib Khan     for (const auto& intf : activations)
566b9da6634SSaqib Khan     {
567b9da6634SSaqib Khan         if (intf.second->redundancyPriority)
568b9da6634SSaqib Khan         {
569b9da6634SSaqib Khan             if (intf.second->redundancyPriority.get()->priority() < value)
570b9da6634SSaqib Khan             {
571b9da6634SSaqib Khan                 return false;
572b9da6634SSaqib Khan             }
573b9da6634SSaqib Khan         }
574b9da6634SSaqib Khan     }
575b9da6634SSaqib Khan     return true;
576b9da6634SSaqib Khan }
577b9da6634SSaqib Khan 
578b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId)
579b77551cdSAdriana Kobylak {
58056aaf454SLei YU     helper.updateUbootVersionId(versionId);
581b77551cdSAdriana Kobylak }
582b77551cdSAdriana Kobylak 
58349446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars()
58449446ae9SSaqib Khan {
58549446ae9SSaqib Khan     decltype(activations.begin()->second->redundancyPriority.get()->priority())
58649446ae9SSaqib Khan         lowestPriority = std::numeric_limits<uint8_t>::max();
58749446ae9SSaqib Khan     decltype(activations.begin()->second->versionId) lowestPriorityVersion;
58849446ae9SSaqib Khan     for (const auto& intf : activations)
58949446ae9SSaqib Khan     {
59049446ae9SSaqib Khan         if (!intf.second->redundancyPriority.get())
59149446ae9SSaqib Khan         {
59249446ae9SSaqib Khan             // Skip this version if the redundancyPriority is not initialized.
59349446ae9SSaqib Khan             continue;
59449446ae9SSaqib Khan         }
59549446ae9SSaqib Khan 
5962285fe0fSAdriana Kobylak         if (intf.second->redundancyPriority.get()->priority() <= lowestPriority)
59749446ae9SSaqib Khan         {
59849446ae9SSaqib Khan             lowestPriority = intf.second->redundancyPriority.get()->priority();
59949446ae9SSaqib Khan             lowestPriorityVersion = intf.second->versionId;
60049446ae9SSaqib Khan         }
60149446ae9SSaqib Khan     }
60249446ae9SSaqib Khan 
603f0382c35SSaqib Khan     // Update the U-boot environment variable to point to the lowest priority
604b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestPriorityVersion);
60549446ae9SSaqib Khan }
60649446ae9SSaqib Khan 
607a6963590SAdriana Kobylak void ItemUpdater::freeSpace(Activation& caller)
608204e1e74SAdriana Kobylak {
609204e1e74SAdriana Kobylak     //  Versions with the highest priority in front
610204e1e74SAdriana Kobylak     std::priority_queue<std::pair<int, std::string>,
611204e1e74SAdriana Kobylak                         std::vector<std::pair<int, std::string>>,
6122285fe0fSAdriana Kobylak                         std::less<std::pair<int, std::string>>>
6132285fe0fSAdriana Kobylak         versionsPQ;
614204e1e74SAdriana Kobylak 
615204e1e74SAdriana Kobylak     std::size_t count = 0;
616204e1e74SAdriana Kobylak     for (const auto& iter : activations)
617204e1e74SAdriana Kobylak     {
618204e1e74SAdriana Kobylak         if ((iter.second.get()->activation() ==
619204e1e74SAdriana Kobylak              server::Activation::Activations::Active) ||
620204e1e74SAdriana Kobylak             (iter.second.get()->activation() ==
621204e1e74SAdriana Kobylak              server::Activation::Activations::Failed))
622204e1e74SAdriana Kobylak         {
623204e1e74SAdriana Kobylak             count++;
624204e1e74SAdriana Kobylak             // Don't put the functional version on the queue since we can't
625204e1e74SAdriana Kobylak             // remove the "running" BMC version.
6260f88b5afSLei YU             // If ACTIVE_BMC_MAX_ALLOWED <= 1, there is only one active BMC,
6270f88b5afSLei YU             // so remove functional version as well.
628a6963590SAdriana Kobylak             // Don't delete the the Activation object that called this function.
629a6963590SAdriana Kobylak             if ((versions.find(iter.second->versionId)
630a6963590SAdriana Kobylak                      ->second->isFunctional() &&
631a6963590SAdriana Kobylak                  ACTIVE_BMC_MAX_ALLOWED > 1) ||
632a6963590SAdriana Kobylak                 (iter.second->versionId == caller.versionId))
633204e1e74SAdriana Kobylak             {
634204e1e74SAdriana Kobylak                 continue;
635204e1e74SAdriana Kobylak             }
636a6963590SAdriana Kobylak 
637a6963590SAdriana Kobylak             // Failed activations don't have priority, assign them a large value
638a6963590SAdriana Kobylak             // for sorting purposes.
639a6963590SAdriana Kobylak             auto priority = 999;
640a6963590SAdriana Kobylak             if (iter.second.get()->activation() ==
641a6963590SAdriana Kobylak                 server::Activation::Activations::Active)
642a6963590SAdriana Kobylak             {
643a6963590SAdriana Kobylak                 priority = iter.second->redundancyPriority.get()->priority();
644a6963590SAdriana Kobylak             }
645a6963590SAdriana Kobylak 
646a6963590SAdriana Kobylak             versionsPQ.push(std::make_pair(priority, iter.second->versionId));
647204e1e74SAdriana Kobylak         }
648204e1e74SAdriana Kobylak     }
649204e1e74SAdriana Kobylak 
650204e1e74SAdriana Kobylak     // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1,
651204e1e74SAdriana Kobylak     // remove the highest priority one(s).
652204e1e74SAdriana Kobylak     while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty()))
653204e1e74SAdriana Kobylak     {
654204e1e74SAdriana Kobylak         erase(versionsPQ.top().second);
655204e1e74SAdriana Kobylak         versionsPQ.pop();
656204e1e74SAdriana Kobylak         count--;
657204e1e74SAdriana Kobylak     }
658204e1e74SAdriana Kobylak }
659204e1e74SAdriana Kobylak 
660eaa1ee05SEddie James void ItemUpdater::mirrorUbootToAlt()
661eaa1ee05SEddie James {
66256aaf454SLei YU     helper.mirrorAlt();
663eaa1ee05SEddie James }
664eaa1ee05SEddie James 
665ec1b41c4SGunnar Mills } // namespace updater
666ec1b41c4SGunnar Mills } // namespace software
667ec1b41c4SGunnar Mills } // namespace phosphor
668