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 
10c98d912eSAdriana Kobylak #include <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>
18*60f5ccfdSGunnar Mills #include <thread>
19d5b8f75cSAdriana Kobylak #include <xyz/openbmc_project/Common/error.hpp>
2043699ca7SAdriana Kobylak #include <xyz/openbmc_project/Software/Image/error.hpp>
21ec1b41c4SGunnar Mills 
22ec1b41c4SGunnar Mills namespace phosphor
23ec1b41c4SGunnar Mills {
24ec1b41c4SGunnar Mills namespace software
25ec1b41c4SGunnar Mills {
26ec1b41c4SGunnar Mills namespace updater
27ec1b41c4SGunnar Mills {
28ec1b41c4SGunnar Mills 
292ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class
302ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server;
310129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server;
322ce7da29SGunnar Mills 
332ce7da29SGunnar Mills using namespace phosphor::logging;
3443699ca7SAdriana Kobylak using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error;
352ab9b109SJayanth Othayoth using namespace phosphor::software::image;
36c98d912eSAdriana Kobylak namespace fs = std::filesystem;
37d5b8f75cSAdriana Kobylak using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
3835e83f3eSSaqib Khan 
39e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg)
40ec1b41c4SGunnar Mills {
4184a0e693SSaqib Khan 
4284a0e693SSaqib Khan     using SVersion = server::Version;
4384a0e693SSaqib Khan     using VersionPurpose = SVersion::VersionPurpose;
449a782243SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
4584a0e693SSaqib Khan     namespace mesg = sdbusplus::message;
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(
65e883fb8bSPatrick Williams                         std::get<std::string>(property.second));
6684a0e693SSaqib Khan                     if (value == VersionPurpose::BMC ||
67e9f6c845SVijay Khemka #ifdef HOST_BIOS_UPGRADE
68e9f6c845SVijay Khemka                         value == VersionPurpose::Host ||
69e9f6c845SVijay Khemka #endif
7084a0e693SSaqib Khan                         value == VersionPurpose::System)
7184a0e693SSaqib Khan                     {
7284a0e693SSaqib Khan                         purpose = value;
7384a0e693SSaqib Khan                     }
74705f1bfcSSaqib Khan                 }
75705f1bfcSSaqib Khan                 else if (property.first == "Version")
76705f1bfcSSaqib Khan                 {
77e883fb8bSPatrick Williams                     version = std::get<std::string>(property.second);
78705f1bfcSSaqib Khan                 }
79705f1bfcSSaqib Khan             }
80705f1bfcSSaqib Khan         }
8119177d3eSSaqib Khan         else if (intf.first == FILEPATH_IFACE)
8219177d3eSSaqib Khan         {
8319177d3eSSaqib Khan             for (const auto& property : intf.second)
8419177d3eSSaqib Khan             {
8519177d3eSSaqib Khan                 if (property.first == "Path")
8619177d3eSSaqib Khan                 {
87e883fb8bSPatrick Williams                     filePath = std::get<std::string>(property.second);
8819177d3eSSaqib Khan                 }
8919177d3eSSaqib Khan             }
9019177d3eSSaqib Khan         }
91705f1bfcSSaqib Khan     }
922285fe0fSAdriana Kobylak     if (version.empty() || filePath.empty() ||
9384a0e693SSaqib Khan         purpose == VersionPurpose::Unknown)
942ce7da29SGunnar Mills     {
95e75d10f5SPatrick Williams         return;
962ce7da29SGunnar Mills     }
972ce7da29SGunnar Mills 
982ce7da29SGunnar Mills     // Version id is the last item in the path
992ce7da29SGunnar Mills     auto pos = path.rfind("/");
1002ce7da29SGunnar Mills     if (pos == std::string::npos)
1012ce7da29SGunnar Mills     {
1022ce7da29SGunnar Mills         log<level::ERR>("No version id found in object path",
103596466b8SAdriana Kobylak                         entry("OBJPATH=%s", path.c_str()));
104e75d10f5SPatrick Williams         return;
1052ce7da29SGunnar Mills     }
1062ce7da29SGunnar Mills 
1072ce7da29SGunnar Mills     auto versionId = path.substr(pos + 1);
1082ce7da29SGunnar Mills 
109e75d10f5SPatrick Williams     if (activations.find(versionId) == activations.end())
1102ce7da29SGunnar Mills     {
11135e83f3eSSaqib Khan         // Determine the Activation state by processing the given image dir.
11235e83f3eSSaqib Khan         auto activationState = server::Activation::Activations::Invalid;
113e9f6c845SVijay Khemka         ItemUpdater::ActivationStatus result;
114e9f6c845SVijay Khemka         if (purpose == VersionPurpose::BMC || purpose == VersionPurpose::System)
115e9f6c845SVijay Khemka             result = ItemUpdater::validateSquashFSImage(filePath);
116e9f6c845SVijay Khemka         else
117e9f6c845SVijay Khemka             result = ItemUpdater::ActivationStatus::ready;
118e9f6c845SVijay Khemka 
11943b25cdeSGunnar Mills         AssociationList associations = {};
12043b25cdeSGunnar Mills 
12135e83f3eSSaqib Khan         if (result == ItemUpdater::ActivationStatus::ready)
12235e83f3eSSaqib Khan         {
12335e83f3eSSaqib Khan             activationState = server::Activation::Activations::Ready;
124b60add1eSGunnar Mills             // Create an association to the BMC inventory item
1252285fe0fSAdriana Kobylak             associations.emplace_back(
1262285fe0fSAdriana Kobylak                 std::make_tuple(ACTIVATION_FWD_ASSOCIATION,
1272285fe0fSAdriana Kobylak                                 ACTIVATION_REV_ASSOCIATION, bmcInventoryPath));
12843b25cdeSGunnar Mills         }
129b60add1eSGunnar Mills 
130ee13e831SSaqib Khan         activations.insert(std::make_pair(
131ee13e831SSaqib Khan             versionId,
1322285fe0fSAdriana Kobylak             std::make_unique<Activation>(bus, path, *this, versionId,
1332285fe0fSAdriana Kobylak                                          activationState, associations)));
1344254beceSMichael Tritz 
135ee13e831SSaqib Khan         auto versionPtr = std::make_unique<VersionClass>(
1362285fe0fSAdriana Kobylak             bus, path, version, purpose, filePath,
1372285fe0fSAdriana Kobylak             std::bind(&ItemUpdater::erase, this, std::placeholders::_1));
138ee13e831SSaqib Khan         versionPtr->deleteObject =
1392285fe0fSAdriana Kobylak             std::make_unique<phosphor::software::manager::Delete>(bus, path,
1402285fe0fSAdriana Kobylak                                                                   *versionPtr);
141ee13e831SSaqib Khan         versions.insert(std::make_pair(versionId, std::move(versionPtr)));
1422ce7da29SGunnar Mills     }
143e75d10f5SPatrick Williams     return;
144ec1b41c4SGunnar Mills }
145ec1b41c4SGunnar Mills 
146ba239881SSaqib Khan void ItemUpdater::processBMCImage()
147ba239881SSaqib Khan {
14888e8a325SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
149269bff30SLei YU 
150269bff30SLei YU     // Check MEDIA_DIR and create if it does not exist
151269bff30SLei YU     try
152269bff30SLei YU     {
153269bff30SLei YU         if (!fs::is_directory(MEDIA_DIR))
154269bff30SLei YU         {
155269bff30SLei YU             fs::create_directory(MEDIA_DIR);
156269bff30SLei YU         }
157269bff30SLei YU     }
158269bff30SLei YU     catch (const fs::filesystem_error& e)
159269bff30SLei YU     {
160269bff30SLei YU         log<level::ERR>("Failed to prepare dir", entry("ERR=%s", e.what()));
161269bff30SLei YU         return;
162269bff30SLei YU     }
163269bff30SLei YU 
16488e8a325SGunnar Mills     // Read os-release from /etc/ to get the functional BMC version
16588e8a325SGunnar Mills     auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE);
16688e8a325SGunnar Mills 
1671eef62deSSaqib Khan     // Read os-release from folders under /media/ to get
1681eef62deSSaqib Khan     // BMC Software Versions.
1691eef62deSSaqib Khan     for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
1701eef62deSSaqib Khan     {
1711eef62deSSaqib Khan         auto activationState = server::Activation::Activations::Active;
1726fab70daSSaqib Khan         static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX);
1731eef62deSSaqib Khan 
1741eef62deSSaqib Khan         // Check if the BMC_RO_PREFIXis the prefix of the iter.path
1752285fe0fSAdriana Kobylak         if (0 ==
1762285fe0fSAdriana Kobylak             iter.path().native().compare(0, BMC_RO_PREFIX_LEN, BMC_ROFS_PREFIX))
1771eef62deSSaqib Khan         {
178021c365bSSaqib Khan             // The versionId is extracted from the path
179021c365bSSaqib Khan             // for example /media/ro-2a1022fe.
180021c365bSSaqib Khan             auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
1811eef62deSSaqib Khan             auto osRelease = iter.path() / OS_RELEASE_FILE;
1821eef62deSSaqib Khan             if (!fs::is_regular_file(osRelease))
1831eef62deSSaqib Khan             {
1842285fe0fSAdriana Kobylak                 log<level::ERR>(
1852285fe0fSAdriana Kobylak                     "Failed to read osRelease",
186596466b8SAdriana Kobylak                     entry("FILENAME=%s", osRelease.string().c_str()));
187021c365bSSaqib Khan                 ItemUpdater::erase(id);
188021c365bSSaqib Khan                 continue;
1891eef62deSSaqib Khan             }
19088e8a325SGunnar Mills             auto version = VersionClass::getBMCVersion(osRelease);
1911eef62deSSaqib Khan             if (version.empty())
1921eef62deSSaqib Khan             {
1932285fe0fSAdriana Kobylak                 log<level::ERR>(
1942285fe0fSAdriana Kobylak                     "Failed to read version from osRelease",
195596466b8SAdriana Kobylak                     entry("FILENAME=%s", osRelease.string().c_str()));
1961eef62deSSaqib Khan                 activationState = server::Activation::Activations::Invalid;
1971eef62deSSaqib Khan             }
198021c365bSSaqib Khan 
1991eef62deSSaqib Khan             auto purpose = server::Version::VersionPurpose::BMC;
200ec4eec34SAdriana Kobylak             restorePurpose(id, purpose);
201ec4eec34SAdriana Kobylak 
2021eef62deSSaqib Khan             auto path = fs::path(SOFTWARE_OBJPATH) / id;
2031eef62deSSaqib Khan 
204269bff30SLei YU             // Create functional association if this is the functional
205269bff30SLei YU             // version
20688e8a325SGunnar Mills             if (version.compare(functionalVersion) == 0)
20788e8a325SGunnar Mills             {
20888e8a325SGunnar Mills                 createFunctionalAssociation(path);
20988e8a325SGunnar Mills             }
21088e8a325SGunnar Mills 
21143b25cdeSGunnar Mills             AssociationList associations = {};
21243b25cdeSGunnar Mills 
21343b25cdeSGunnar Mills             if (activationState == server::Activation::Activations::Active)
21443b25cdeSGunnar Mills             {
21543b25cdeSGunnar Mills                 // Create an association to the BMC inventory item
21643b25cdeSGunnar Mills                 associations.emplace_back(std::make_tuple(
2172285fe0fSAdriana Kobylak                     ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
21843b25cdeSGunnar Mills                     bmcInventoryPath));
21943b25cdeSGunnar Mills 
22043b25cdeSGunnar Mills                 // Create an active association since this image is active
22143b25cdeSGunnar Mills                 createActiveAssociation(path);
22243b25cdeSGunnar Mills             }
22343b25cdeSGunnar Mills 
224bbebec79SAppaRao Puli             // All updateable firmware components must expose the updateable
225bbebec79SAppaRao Puli             // association.
226bbebec79SAppaRao Puli             createUpdateableAssociation(path);
227bbebec79SAppaRao Puli 
228ee590c74SAdriana Kobylak             // Create Version instance for this version.
229ee590c74SAdriana Kobylak             auto versionPtr = std::make_unique<VersionClass>(
2302285fe0fSAdriana Kobylak                 bus, path, version, purpose, "",
2312285fe0fSAdriana Kobylak                 std::bind(&ItemUpdater::erase, this, std::placeholders::_1));
232ee590c74SAdriana Kobylak             auto isVersionFunctional = versionPtr->isFunctional();
233ee13e831SSaqib Khan             if (!isVersionFunctional)
234ee13e831SSaqib Khan             {
235ee13e831SSaqib Khan                 versionPtr->deleteObject =
236ee13e831SSaqib Khan                     std::make_unique<phosphor::software::manager::Delete>(
237ee13e831SSaqib Khan                         bus, path, *versionPtr);
238ee13e831SSaqib Khan             }
2392285fe0fSAdriana Kobylak             versions.insert(std::make_pair(id, std::move(versionPtr)));
240ee590c74SAdriana Kobylak 
2411eef62deSSaqib Khan             // Create Activation instance for this version.
242ee13e831SSaqib Khan             activations.insert(std::make_pair(
2432285fe0fSAdriana Kobylak                 id, std::make_unique<Activation>(
2442285fe0fSAdriana Kobylak                         bus, path, *this, id, activationState, associations)));
2451eef62deSSaqib Khan 
246269bff30SLei YU             // If Active, create RedundancyPriority instance for this
247269bff30SLei YU             // version.
2481eef62deSSaqib Khan             if (activationState == server::Activation::Activations::Active)
2491eef62deSSaqib Khan             {
2501eef62deSSaqib Khan                 uint8_t priority = std::numeric_limits<uint8_t>::max();
251687e75e2SAdriana Kobylak                 if (!restorePriority(id, priority))
2521eef62deSSaqib Khan                 {
253ee590c74SAdriana Kobylak                     if (isVersionFunctional)
254ee590c74SAdriana Kobylak                     {
255ee590c74SAdriana Kobylak                         priority = 0;
256ee590c74SAdriana Kobylak                     }
257ee590c74SAdriana Kobylak                     else
258ee590c74SAdriana Kobylak                     {
2591eef62deSSaqib Khan                         log<level::ERR>("Unable to restore priority from file.",
260596466b8SAdriana Kobylak                                         entry("VERSIONID=%s", id.c_str()));
2611eef62deSSaqib Khan                     }
262ee590c74SAdriana Kobylak                 }
2631eef62deSSaqib Khan                 activations.find(id)->second->redundancyPriority =
2641eef62deSSaqib Khan                     std::make_unique<RedundancyPriority>(
2652285fe0fSAdriana Kobylak                         bus, path, *(activations.find(id)->second), priority,
266b77551cdSAdriana Kobylak                         false);
2671eef62deSSaqib Khan             }
2681eef62deSSaqib Khan         }
2691eef62deSSaqib Khan     }
270dcbfa04aSSaqib Khan 
271dcbfa04aSSaqib Khan     // If there is no ubi volume for bmc version then read the /etc/os-release
272dcbfa04aSSaqib Khan     // and create rofs-<versionId> under /media
273dcbfa04aSSaqib Khan     if (activations.size() == 0)
274dcbfa04aSSaqib Khan     {
275d16bcbd5SGunnar Mills         auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE);
276dcbfa04aSSaqib Khan         auto id = phosphor::software::manager::Version::getId(version);
277dcbfa04aSSaqib Khan         auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/";
278dcbfa04aSSaqib Khan         try
279dcbfa04aSSaqib Khan         {
280dcbfa04aSSaqib Khan             if (!fs::is_directory(versionFileDir))
281dcbfa04aSSaqib Khan             {
282dcbfa04aSSaqib Khan                 fs::create_directories(versionFileDir);
283dcbfa04aSSaqib Khan             }
284dcbfa04aSSaqib Khan             auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE;
285dcbfa04aSSaqib Khan             fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath);
286dcbfa04aSSaqib Khan             ItemUpdater::processBMCImage();
287dcbfa04aSSaqib Khan         }
288dcbfa04aSSaqib Khan         catch (const std::exception& e)
289dcbfa04aSSaqib Khan         {
290dcbfa04aSSaqib Khan             log<level::ERR>(e.what());
291dcbfa04aSSaqib Khan         }
292dcbfa04aSSaqib Khan     }
293eaa1ee05SEddie James 
294eaa1ee05SEddie James     mirrorUbootToAlt();
295ba239881SSaqib Khan     return;
296ba239881SSaqib Khan }
297ba239881SSaqib Khan 
2983526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId)
2993526ef73SLeonel Gonzalez {
3006d873715SEddie James     // Find entry in versions map
3016d873715SEddie James     auto it = versions.find(entryId);
3026d873715SEddie James     if (it != versions.end())
3036d873715SEddie James     {
3040f88b5afSLei YU         if (it->second->isFunctional() && ACTIVE_BMC_MAX_ALLOWED > 1)
3056d873715SEddie James         {
3062285fe0fSAdriana Kobylak             log<level::ERR>("Error: Version is currently running on the BMC. "
3072285fe0fSAdriana Kobylak                             "Unable to remove.",
3082285fe0fSAdriana Kobylak                             entry("VERSIONID=%s", entryId.c_str()));
3096d873715SEddie James             return;
3106d873715SEddie James         }
3116d873715SEddie James 
3126d873715SEddie James         // Delete ReadOnly partitions if it's not active
3133526ef73SLeonel Gonzalez         removeReadOnlyPartition(entryId);
314687e75e2SAdriana Kobylak         removePersistDataDirectory(entryId);
315ee13e831SSaqib Khan 
316ee13e831SSaqib Khan         // Removing entry in versions map
317ee13e831SSaqib Khan         this->versions.erase(entryId);
3186d873715SEddie James     }
3196d873715SEddie James     else
3206d873715SEddie James     {
3216d873715SEddie James         // Delete ReadOnly partitions even if we can't find the version
3226d873715SEddie James         removeReadOnlyPartition(entryId);
323687e75e2SAdriana Kobylak         removePersistDataDirectory(entryId);
3246d873715SEddie James 
3252285fe0fSAdriana Kobylak         log<level::ERR>("Error: Failed to find version in item updater "
3262285fe0fSAdriana Kobylak                         "versions map. Unable to remove.",
3272285fe0fSAdriana Kobylak                         entry("VERSIONID=%s", entryId.c_str()));
3286d873715SEddie James     }
3291eef62deSSaqib Khan 
33056aaf454SLei YU     helper.clearEntry(entryId);
3313526ef73SLeonel Gonzalez 
3323526ef73SLeonel Gonzalez     // Removing entry in activations map
3333526ef73SLeonel Gonzalez     auto ita = activations.find(entryId);
3343526ef73SLeonel Gonzalez     if (ita == activations.end())
3353526ef73SLeonel Gonzalez     {
3362285fe0fSAdriana Kobylak         log<level::ERR>("Error: Failed to find version in item updater "
3372285fe0fSAdriana Kobylak                         "activations map. Unable to remove.",
3382285fe0fSAdriana Kobylak                         entry("VERSIONID=%s", entryId.c_str()));
3393526ef73SLeonel Gonzalez     }
340ee13e831SSaqib Khan     else
341ee13e831SSaqib Khan     {
342991af7ecSAdriana Kobylak         removeAssociations(ita->second->path);
3433526ef73SLeonel Gonzalez         this->activations.erase(entryId);
344ee13e831SSaqib Khan     }
34549446ae9SSaqib Khan     ItemUpdater::resetUbootEnvVars();
346ee13e831SSaqib Khan     return;
3473526ef73SLeonel Gonzalez }
3483526ef73SLeonel Gonzalez 
349bc1bf3afSMichael Tritz void ItemUpdater::deleteAll()
350bc1bf3afSMichael Tritz {
35183cd21fbSAdriana Kobylak     std::vector<std::string> deletableVersions;
35283cd21fbSAdriana Kobylak 
353bc1bf3afSMichael Tritz     for (const auto& versionIt : versions)
354bc1bf3afSMichael Tritz     {
355bc1bf3afSMichael Tritz         if (!versionIt.second->isFunctional())
356bc1bf3afSMichael Tritz         {
35783cd21fbSAdriana Kobylak             deletableVersions.push_back(versionIt.first);
358bc1bf3afSMichael Tritz         }
359bc1bf3afSMichael Tritz     }
360bc1bf3afSMichael Tritz 
36183cd21fbSAdriana Kobylak     for (const auto& deletableIt : deletableVersions)
36283cd21fbSAdriana Kobylak     {
36383cd21fbSAdriana Kobylak         ItemUpdater::erase(deletableIt);
36483cd21fbSAdriana Kobylak     }
36583cd21fbSAdriana Kobylak 
36656aaf454SLei YU     helper.cleanup();
367bc1bf3afSMichael Tritz }
368bc1bf3afSMichael Tritz 
3692285fe0fSAdriana Kobylak ItemUpdater::ActivationStatus
3702285fe0fSAdriana Kobylak     ItemUpdater::validateSquashFSImage(const std::string& filePath)
37135e83f3eSSaqib Khan {
372b1cfdf99SMichael Tritz     bool invalid = false;
37335e83f3eSSaqib Khan 
374b1cfdf99SMichael Tritz     for (auto& bmcImage : bmcImages)
375b1cfdf99SMichael Tritz     {
37619177d3eSSaqib Khan         fs::path file(filePath);
37735e83f3eSSaqib Khan         file /= bmcImage;
37835e83f3eSSaqib Khan         std::ifstream efile(file.c_str());
379b1cfdf99SMichael Tritz         if (efile.good() != 1)
38035e83f3eSSaqib Khan         {
381b1cfdf99SMichael Tritz             log<level::ERR>("Failed to find the BMC image.",
382b1cfdf99SMichael Tritz                             entry("IMAGE=%s", bmcImage.c_str()));
383b1cfdf99SMichael Tritz             invalid = true;
38435e83f3eSSaqib Khan         }
385b1cfdf99SMichael Tritz     }
386b1cfdf99SMichael Tritz 
387b1cfdf99SMichael Tritz     if (invalid)
38835e83f3eSSaqib Khan     {
38935e83f3eSSaqib Khan         return ItemUpdater::ActivationStatus::invalid;
39035e83f3eSSaqib Khan     }
391b1cfdf99SMichael Tritz 
392b1cfdf99SMichael Tritz     return ItemUpdater::ActivationStatus::ready;
39335e83f3eSSaqib Khan }
39435e83f3eSSaqib Khan 
395bbcb7be1SAdriana Kobylak void ItemUpdater::savePriority(const std::string& versionId, uint8_t value)
396bbcb7be1SAdriana Kobylak {
397687e75e2SAdriana Kobylak     storePriority(versionId, value);
398bbcb7be1SAdriana Kobylak     helper.setEntry(versionId, value);
399bbcb7be1SAdriana Kobylak }
400bbcb7be1SAdriana Kobylak 
401b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
4024c1aec09SSaqib Khan {
403b77551cdSAdriana Kobylak     std::map<std::string, uint8_t> priorityMap;
404b77551cdSAdriana Kobylak 
405b77551cdSAdriana Kobylak     // Insert the requested version and priority, it may not exist yet.
406b77551cdSAdriana Kobylak     priorityMap.insert(std::make_pair(versionId, value));
407b77551cdSAdriana Kobylak 
4084c1aec09SSaqib Khan     for (const auto& intf : activations)
4094c1aec09SSaqib Khan     {
4104c1aec09SSaqib Khan         if (intf.second->redundancyPriority)
4114c1aec09SSaqib Khan         {
412b77551cdSAdriana Kobylak             priorityMap.insert(std::make_pair(
4132285fe0fSAdriana Kobylak                 intf.first, intf.second->redundancyPriority.get()->priority()));
414b77551cdSAdriana Kobylak         }
415b77551cdSAdriana Kobylak     }
416b77551cdSAdriana Kobylak 
417b77551cdSAdriana Kobylak     // Lambda function to compare 2 priority values, use <= to allow duplicates
4182285fe0fSAdriana Kobylak     typedef std::function<bool(std::pair<std::string, uint8_t>,
4192285fe0fSAdriana Kobylak                                std::pair<std::string, uint8_t>)>
4202285fe0fSAdriana Kobylak         cmpPriority;
4212285fe0fSAdriana Kobylak     cmpPriority cmpPriorityFunc =
4222285fe0fSAdriana Kobylak         [](std::pair<std::string, uint8_t> priority1,
4232285fe0fSAdriana Kobylak            std::pair<std::string, uint8_t> priority2) {
424b77551cdSAdriana Kobylak             return priority1.second <= priority2.second;
425b77551cdSAdriana Kobylak         };
426b77551cdSAdriana Kobylak 
427b77551cdSAdriana Kobylak     // Sort versions by ascending priority
428b77551cdSAdriana Kobylak     std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet(
429b77551cdSAdriana Kobylak         priorityMap.begin(), priorityMap.end(), cmpPriorityFunc);
430b77551cdSAdriana Kobylak 
431b77551cdSAdriana Kobylak     auto freePriorityValue = value;
432b77551cdSAdriana Kobylak     for (auto& element : prioritySet)
433b77551cdSAdriana Kobylak     {
434b77551cdSAdriana Kobylak         if (element.first == versionId)
435b77551cdSAdriana Kobylak         {
436b77551cdSAdriana Kobylak             continue;
437b77551cdSAdriana Kobylak         }
438b77551cdSAdriana Kobylak         if (element.second == freePriorityValue)
439b77551cdSAdriana Kobylak         {
440b77551cdSAdriana Kobylak             ++freePriorityValue;
441b77551cdSAdriana Kobylak             auto it = activations.find(element.first);
442b77551cdSAdriana Kobylak             it->second->redundancyPriority.get()->sdbusPriority(
443b77551cdSAdriana Kobylak                 freePriorityValue);
4444c1aec09SSaqib Khan         }
4454c1aec09SSaqib Khan     }
446b77551cdSAdriana Kobylak 
447b77551cdSAdriana Kobylak     auto lowestVersion = prioritySet.begin()->first;
448b77551cdSAdriana Kobylak     if (value == prioritySet.begin()->second)
449b77551cdSAdriana Kobylak     {
450b77551cdSAdriana Kobylak         lowestVersion = versionId;
4514c1aec09SSaqib Khan     }
452b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestVersion);
4534c1aec09SSaqib Khan }
4544c1aec09SSaqib Khan 
45537a59043SMichael Tritz void ItemUpdater::reset()
45637a59043SMichael Tritz {
457*60f5ccfdSGunnar Mills     constexpr auto setFactoryResetWait = std::chrono::seconds(3);
45856aaf454SLei YU     helper.factoryReset();
45937a59043SMichael Tritz 
460*60f5ccfdSGunnar Mills     // Need to wait for env variables to complete, otherwise an immediate reboot
461*60f5ccfdSGunnar Mills     // will not factory reset.
462*60f5ccfdSGunnar Mills     std::this_thread::sleep_for(setFactoryResetWait);
463*60f5ccfdSGunnar Mills 
46437a59043SMichael Tritz     log<level::INFO>("BMC factory reset will take effect upon reboot.");
46537a59043SMichael Tritz }
46637a59043SMichael Tritz 
4673526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId)
4683526ef73SLeonel Gonzalez {
46956aaf454SLei YU     helper.removeVersion(versionId);
4703526ef73SLeonel Gonzalez }
4713526ef73SLeonel Gonzalez 
4720129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value)
4730129d926SMichael Tritz {
4740129d926SMichael Tritz     // enabling field mode is intended to be one way: false -> true
4750129d926SMichael Tritz     if (value && !control::FieldMode::fieldModeEnabled())
4760129d926SMichael Tritz     {
4770129d926SMichael Tritz         control::FieldMode::fieldModeEnabled(value);
4780129d926SMichael Tritz 
47922848eceSAdriana Kobylak         auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
48022848eceSAdriana Kobylak                                           SYSTEMD_INTERFACE, "StartUnit");
48122848eceSAdriana Kobylak         method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service",
48222848eceSAdriana Kobylak                       "replace");
48322848eceSAdriana Kobylak         bus.call_noreply(method);
48422848eceSAdriana Kobylak 
48522848eceSAdriana Kobylak         method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
48622848eceSAdriana Kobylak                                      SYSTEMD_INTERFACE, "StopUnit");
48722848eceSAdriana Kobylak         method.append("usr-local.mount", "replace");
48822848eceSAdriana Kobylak         bus.call_noreply(method);
48922848eceSAdriana Kobylak 
49022848eceSAdriana Kobylak         std::vector<std::string> usrLocal = {"usr-local.mount"};
49122848eceSAdriana Kobylak 
49222848eceSAdriana Kobylak         method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
49322848eceSAdriana Kobylak                                      SYSTEMD_INTERFACE, "MaskUnitFiles");
49422848eceSAdriana Kobylak         method.append(usrLocal, false, true);
49522848eceSAdriana Kobylak         bus.call_noreply(method);
4960129d926SMichael Tritz     }
497d5b8f75cSAdriana Kobylak     else if (!value && control::FieldMode::fieldModeEnabled())
498d5b8f75cSAdriana Kobylak     {
499d5b8f75cSAdriana Kobylak         elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON(
500d5b8f75cSAdriana Kobylak             "FieldMode is not allowed to be cleared"));
501d5b8f75cSAdriana Kobylak     }
5020129d926SMichael Tritz 
5030129d926SMichael Tritz     return control::FieldMode::fieldModeEnabled();
5040129d926SMichael Tritz }
5050129d926SMichael Tritz 
5060129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus()
5070129d926SMichael Tritz {
508ff0b421dSMichael Tritz     std::ifstream input("/dev/mtd/u-boot-env");
5090129d926SMichael Tritz     std::string envVar;
5100129d926SMichael Tritz     std::getline(input, envVar);
5110129d926SMichael Tritz 
5120129d926SMichael Tritz     if (envVar.find("fieldmode=true") != std::string::npos)
5130129d926SMichael Tritz     {
5140129d926SMichael Tritz         ItemUpdater::fieldModeEnabled(true);
5150129d926SMichael Tritz     }
5160129d926SMichael Tritz }
5170129d926SMichael Tritz 
518b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath()
519b60add1eSGunnar Mills {
520b60add1eSGunnar Mills     auto depth = 0;
5212285fe0fSAdriana Kobylak     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
5222285fe0fSAdriana Kobylak                                           MAPPER_INTERFACE, "GetSubTreePaths");
523b60add1eSGunnar Mills 
5241254c628SAdriana Kobylak     mapperCall.append(INVENTORY_PATH);
525b60add1eSGunnar Mills     mapperCall.append(depth);
5261254c628SAdriana Kobylak     std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE};
527b60add1eSGunnar Mills     mapperCall.append(filter);
528b60add1eSGunnar Mills 
52987c78173SEd Tanous     try
530b60add1eSGunnar Mills     {
53187c78173SEd Tanous         auto response = bus.call(mapperCall);
532b60add1eSGunnar Mills 
533b60add1eSGunnar Mills         using ObjectPaths = std::vector<std::string>;
534b60add1eSGunnar Mills         ObjectPaths result;
535b60add1eSGunnar Mills         response.read(result);
536b60add1eSGunnar Mills 
5371254c628SAdriana Kobylak         if (!result.empty())
538b60add1eSGunnar Mills         {
5391254c628SAdriana Kobylak             bmcInventoryPath = result.front();
540b60add1eSGunnar Mills         }
54187c78173SEd Tanous     }
54287c78173SEd Tanous     catch (const sdbusplus::exception::SdBusError& e)
54387c78173SEd Tanous     {
54487c78173SEd Tanous         log<level::ERR>("Error in mapper GetSubTreePath");
54587c78173SEd Tanous         return;
54687c78173SEd Tanous     }
547b60add1eSGunnar Mills 
548b60add1eSGunnar Mills     return;
549b60add1eSGunnar Mills }
550b60add1eSGunnar Mills 
551f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path)
552ded875dcSGunnar Mills {
5532285fe0fSAdriana Kobylak     assocs.emplace_back(
5542285fe0fSAdriana Kobylak         std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
555ded875dcSGunnar Mills     associations(assocs);
556ded875dcSGunnar Mills }
557ded875dcSGunnar Mills 
55888e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path)
55988e8a325SGunnar Mills {
56088e8a325SGunnar Mills     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
5612285fe0fSAdriana Kobylak                                         FUNCTIONAL_REV_ASSOCIATION, path));
56288e8a325SGunnar Mills     associations(assocs);
56388e8a325SGunnar Mills }
56488e8a325SGunnar Mills 
565bbebec79SAppaRao Puli void ItemUpdater::createUpdateableAssociation(const std::string& path)
566bbebec79SAppaRao Puli {
567bbebec79SAppaRao Puli     assocs.emplace_back(std::make_tuple(UPDATEABLE_FWD_ASSOCIATION,
568bbebec79SAppaRao Puli                                         UPDATEABLE_REV_ASSOCIATION, path));
569bbebec79SAppaRao Puli     associations(assocs);
570bbebec79SAppaRao Puli }
571bbebec79SAppaRao Puli 
572991af7ecSAdriana Kobylak void ItemUpdater::removeAssociations(const std::string& path)
573ded875dcSGunnar Mills {
574ded875dcSGunnar Mills     for (auto iter = assocs.begin(); iter != assocs.end();)
575ded875dcSGunnar Mills     {
576991af7ecSAdriana Kobylak         if ((std::get<2>(*iter)).compare(path) == 0)
577ded875dcSGunnar Mills         {
578ded875dcSGunnar Mills             iter = assocs.erase(iter);
579ded875dcSGunnar Mills             associations(assocs);
580ded875dcSGunnar Mills         }
581ded875dcSGunnar Mills         else
582ded875dcSGunnar Mills         {
583ded875dcSGunnar Mills             ++iter;
584ded875dcSGunnar Mills         }
585ded875dcSGunnar Mills     }
586ded875dcSGunnar Mills }
587ded875dcSGunnar Mills 
588b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value)
589b9da6634SSaqib Khan {
590b9da6634SSaqib Khan     for (const auto& intf : activations)
591b9da6634SSaqib Khan     {
592b9da6634SSaqib Khan         if (intf.second->redundancyPriority)
593b9da6634SSaqib Khan         {
594b9da6634SSaqib Khan             if (intf.second->redundancyPriority.get()->priority() < value)
595b9da6634SSaqib Khan             {
596b9da6634SSaqib Khan                 return false;
597b9da6634SSaqib Khan             }
598b9da6634SSaqib Khan         }
599b9da6634SSaqib Khan     }
600b9da6634SSaqib Khan     return true;
601b9da6634SSaqib Khan }
602b9da6634SSaqib Khan 
603b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId)
604b77551cdSAdriana Kobylak {
60556aaf454SLei YU     helper.updateUbootVersionId(versionId);
606b77551cdSAdriana Kobylak }
607b77551cdSAdriana Kobylak 
60849446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars()
60949446ae9SSaqib Khan {
61049446ae9SSaqib Khan     decltype(activations.begin()->second->redundancyPriority.get()->priority())
61149446ae9SSaqib Khan         lowestPriority = std::numeric_limits<uint8_t>::max();
61249446ae9SSaqib Khan     decltype(activations.begin()->second->versionId) lowestPriorityVersion;
61349446ae9SSaqib Khan     for (const auto& intf : activations)
61449446ae9SSaqib Khan     {
61549446ae9SSaqib Khan         if (!intf.second->redundancyPriority.get())
61649446ae9SSaqib Khan         {
61749446ae9SSaqib Khan             // Skip this version if the redundancyPriority is not initialized.
61849446ae9SSaqib Khan             continue;
61949446ae9SSaqib Khan         }
62049446ae9SSaqib Khan 
6212285fe0fSAdriana Kobylak         if (intf.second->redundancyPriority.get()->priority() <= lowestPriority)
62249446ae9SSaqib Khan         {
62349446ae9SSaqib Khan             lowestPriority = intf.second->redundancyPriority.get()->priority();
62449446ae9SSaqib Khan             lowestPriorityVersion = intf.second->versionId;
62549446ae9SSaqib Khan         }
62649446ae9SSaqib Khan     }
62749446ae9SSaqib Khan 
628f0382c35SSaqib Khan     // Update the U-boot environment variable to point to the lowest priority
629b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestPriorityVersion);
63049446ae9SSaqib Khan }
63149446ae9SSaqib Khan 
632a6963590SAdriana Kobylak void ItemUpdater::freeSpace(Activation& caller)
633204e1e74SAdriana Kobylak {
634204e1e74SAdriana Kobylak     //  Versions with the highest priority in front
635204e1e74SAdriana Kobylak     std::priority_queue<std::pair<int, std::string>,
636204e1e74SAdriana Kobylak                         std::vector<std::pair<int, std::string>>,
6372285fe0fSAdriana Kobylak                         std::less<std::pair<int, std::string>>>
6382285fe0fSAdriana Kobylak         versionsPQ;
639204e1e74SAdriana Kobylak 
640204e1e74SAdriana Kobylak     std::size_t count = 0;
641204e1e74SAdriana Kobylak     for (const auto& iter : activations)
642204e1e74SAdriana Kobylak     {
643204e1e74SAdriana Kobylak         if ((iter.second.get()->activation() ==
644204e1e74SAdriana Kobylak              server::Activation::Activations::Active) ||
645204e1e74SAdriana Kobylak             (iter.second.get()->activation() ==
646204e1e74SAdriana Kobylak              server::Activation::Activations::Failed))
647204e1e74SAdriana Kobylak         {
648204e1e74SAdriana Kobylak             count++;
649204e1e74SAdriana Kobylak             // Don't put the functional version on the queue since we can't
650204e1e74SAdriana Kobylak             // remove the "running" BMC version.
6510f88b5afSLei YU             // If ACTIVE_BMC_MAX_ALLOWED <= 1, there is only one active BMC,
6520f88b5afSLei YU             // so remove functional version as well.
653a6963590SAdriana Kobylak             // Don't delete the the Activation object that called this function.
654a6963590SAdriana Kobylak             if ((versions.find(iter.second->versionId)
655a6963590SAdriana Kobylak                      ->second->isFunctional() &&
656a6963590SAdriana Kobylak                  ACTIVE_BMC_MAX_ALLOWED > 1) ||
657a6963590SAdriana Kobylak                 (iter.second->versionId == caller.versionId))
658204e1e74SAdriana Kobylak             {
659204e1e74SAdriana Kobylak                 continue;
660204e1e74SAdriana Kobylak             }
661a6963590SAdriana Kobylak 
662a6963590SAdriana Kobylak             // Failed activations don't have priority, assign them a large value
663a6963590SAdriana Kobylak             // for sorting purposes.
664a6963590SAdriana Kobylak             auto priority = 999;
665a6963590SAdriana Kobylak             if (iter.second.get()->activation() ==
666a6963590SAdriana Kobylak                 server::Activation::Activations::Active)
667a6963590SAdriana Kobylak             {
668a6963590SAdriana Kobylak                 priority = iter.second->redundancyPriority.get()->priority();
669a6963590SAdriana Kobylak             }
670a6963590SAdriana Kobylak 
671a6963590SAdriana Kobylak             versionsPQ.push(std::make_pair(priority, iter.second->versionId));
672204e1e74SAdriana Kobylak         }
673204e1e74SAdriana Kobylak     }
674204e1e74SAdriana Kobylak 
675204e1e74SAdriana Kobylak     // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1,
676204e1e74SAdriana Kobylak     // remove the highest priority one(s).
677204e1e74SAdriana Kobylak     while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty()))
678204e1e74SAdriana Kobylak     {
679204e1e74SAdriana Kobylak         erase(versionsPQ.top().second);
680204e1e74SAdriana Kobylak         versionsPQ.pop();
681204e1e74SAdriana Kobylak         count--;
682204e1e74SAdriana Kobylak     }
683204e1e74SAdriana Kobylak }
684204e1e74SAdriana Kobylak 
685eaa1ee05SEddie James void ItemUpdater::mirrorUbootToAlt()
686eaa1ee05SEddie James {
68756aaf454SLei YU     helper.mirrorAlt();
688eaa1ee05SEddie James }
689eaa1ee05SEddie James 
690ec1b41c4SGunnar Mills } // namespace updater
691ec1b41c4SGunnar Mills } // namespace software
692ec1b41c4SGunnar Mills } // namespace phosphor
693