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 
10d5b8f75cSAdriana Kobylak #include <phosphor-logging/elog-errors.hpp>
11b0ce996aSGunnar Mills #include <phosphor-logging/elog.hpp>
12b0ce996aSGunnar Mills #include <phosphor-logging/log.hpp>
1358aa7508SAdriana Kobylak #include <xyz/openbmc_project/Common/error.hpp>
1458aa7508SAdriana Kobylak #include <xyz/openbmc_project/Software/Image/error.hpp>
1558aa7508SAdriana Kobylak 
1658aa7508SAdriana Kobylak #include <filesystem>
1758aa7508SAdriana Kobylak #include <fstream>
18204e1e74SAdriana Kobylak #include <queue>
19b77551cdSAdriana Kobylak #include <set>
20ec1b41c4SGunnar Mills #include <string>
2160f5ccfdSGunnar Mills #include <thread>
22ec1b41c4SGunnar Mills 
23ec1b41c4SGunnar Mills namespace phosphor
24ec1b41c4SGunnar Mills {
25ec1b41c4SGunnar Mills namespace software
26ec1b41c4SGunnar Mills {
27ec1b41c4SGunnar Mills namespace updater
28ec1b41c4SGunnar Mills {
29ec1b41c4SGunnar Mills 
302ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class
312ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server;
320129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server;
332ce7da29SGunnar Mills 
342ce7da29SGunnar Mills using namespace phosphor::logging;
3543699ca7SAdriana Kobylak using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error;
362ab9b109SJayanth Othayoth using namespace phosphor::software::image;
37c98d912eSAdriana Kobylak namespace fs = std::filesystem;
38d5b8f75cSAdriana Kobylak using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
3935e83f3eSSaqib Khan 
40e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg)
41ec1b41c4SGunnar Mills {
4284a0e693SSaqib Khan 
4384a0e693SSaqib Khan     using SVersion = server::Version;
4484a0e693SSaqib Khan     using VersionPurpose = SVersion::VersionPurpose;
459a782243SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
4684a0e693SSaqib Khan 
47bc1facd7SPatrick Williams     sdbusplus::message::object_path objPath;
4884a0e693SSaqib Khan     auto purpose = VersionPurpose::Unknown;
49705f1bfcSSaqib Khan     std::string version;
50bc1facd7SPatrick Williams     std::map<std::string, std::map<std::string, std::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         {
178716cd78dSAdriana Kobylak             // Get the version to calculate the id
17924a8d83dSAdriana Kobylak             fs::path releaseFile(OS_RELEASE_FILE);
18024a8d83dSAdriana Kobylak             auto osRelease = iter.path() / releaseFile.relative_path();
1811eef62deSSaqib Khan             if (!fs::is_regular_file(osRelease))
1821eef62deSSaqib Khan             {
1832285fe0fSAdriana Kobylak                 log<level::ERR>(
1842285fe0fSAdriana Kobylak                     "Failed to read osRelease",
185596466b8SAdriana Kobylak                     entry("FILENAME=%s", osRelease.string().c_str()));
186716cd78dSAdriana Kobylak 
187716cd78dSAdriana Kobylak                 // Try to get the version id from the mount directory name and
188716cd78dSAdriana Kobylak                 // call to delete it as this version may be corrupted. Dynamic
189716cd78dSAdriana Kobylak                 // volumes created by the UBI layout for example have the id in
190716cd78dSAdriana Kobylak                 // the mount directory name. The worst that can happen is that
191716cd78dSAdriana Kobylak                 // erase() is called with an non-existent id and returns.
192716cd78dSAdriana Kobylak                 auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
193021c365bSSaqib Khan                 ItemUpdater::erase(id);
194716cd78dSAdriana Kobylak 
195021c365bSSaqib Khan                 continue;
1961eef62deSSaqib Khan             }
19788e8a325SGunnar Mills             auto version = VersionClass::getBMCVersion(osRelease);
1981eef62deSSaqib Khan             if (version.empty())
1991eef62deSSaqib Khan             {
2002285fe0fSAdriana Kobylak                 log<level::ERR>(
2012285fe0fSAdriana Kobylak                     "Failed to read version from osRelease",
202596466b8SAdriana Kobylak                     entry("FILENAME=%s", osRelease.string().c_str()));
203716cd78dSAdriana Kobylak 
204716cd78dSAdriana Kobylak                 // Try to delete the version, same as above if the
205716cd78dSAdriana Kobylak                 // OS_RELEASE_FILE does not exist.
206716cd78dSAdriana Kobylak                 auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
207716cd78dSAdriana Kobylak                 ItemUpdater::erase(id);
208716cd78dSAdriana Kobylak 
209716cd78dSAdriana Kobylak                 continue;
2101eef62deSSaqib Khan             }
211021c365bSSaqib Khan 
212716cd78dSAdriana Kobylak             auto id = VersionClass::getId(version);
213716cd78dSAdriana Kobylak 
214f383d27aSAdriana Kobylak             // Check if the id has already been added. This can happen if the
215f383d27aSAdriana Kobylak             // BMC partitions / devices were manually flashed with the same
216f383d27aSAdriana Kobylak             // image.
217f383d27aSAdriana Kobylak             if (versions.find(id) != versions.end())
218f383d27aSAdriana Kobylak             {
219f383d27aSAdriana Kobylak                 continue;
220f383d27aSAdriana Kobylak             }
221f383d27aSAdriana Kobylak 
2221eef62deSSaqib Khan             auto purpose = server::Version::VersionPurpose::BMC;
223ec4eec34SAdriana Kobylak             restorePurpose(id, purpose);
224ec4eec34SAdriana Kobylak 
2251eef62deSSaqib Khan             auto path = fs::path(SOFTWARE_OBJPATH) / id;
2261eef62deSSaqib Khan 
227269bff30SLei YU             // Create functional association if this is the functional
228269bff30SLei YU             // version
22988e8a325SGunnar Mills             if (version.compare(functionalVersion) == 0)
23088e8a325SGunnar Mills             {
23188e8a325SGunnar Mills                 createFunctionalAssociation(path);
23288e8a325SGunnar Mills             }
23388e8a325SGunnar Mills 
23443b25cdeSGunnar Mills             AssociationList associations = {};
23543b25cdeSGunnar Mills 
23643b25cdeSGunnar Mills             if (activationState == server::Activation::Activations::Active)
23743b25cdeSGunnar Mills             {
23843b25cdeSGunnar Mills                 // Create an association to the BMC inventory item
23943b25cdeSGunnar Mills                 associations.emplace_back(std::make_tuple(
2402285fe0fSAdriana Kobylak                     ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
24143b25cdeSGunnar Mills                     bmcInventoryPath));
24243b25cdeSGunnar Mills 
24343b25cdeSGunnar Mills                 // Create an active association since this image is active
24443b25cdeSGunnar Mills                 createActiveAssociation(path);
24543b25cdeSGunnar Mills             }
24643b25cdeSGunnar Mills 
247bbebec79SAppaRao Puli             // All updateable firmware components must expose the updateable
248bbebec79SAppaRao Puli             // association.
249bbebec79SAppaRao Puli             createUpdateableAssociation(path);
250bbebec79SAppaRao Puli 
251ee590c74SAdriana Kobylak             // Create Version instance for this version.
252ee590c74SAdriana Kobylak             auto versionPtr = std::make_unique<VersionClass>(
2532285fe0fSAdriana Kobylak                 bus, path, version, purpose, "",
2542285fe0fSAdriana Kobylak                 std::bind(&ItemUpdater::erase, this, std::placeholders::_1));
255ee590c74SAdriana Kobylak             auto isVersionFunctional = versionPtr->isFunctional();
256ee13e831SSaqib Khan             if (!isVersionFunctional)
257ee13e831SSaqib Khan             {
258ee13e831SSaqib Khan                 versionPtr->deleteObject =
259ee13e831SSaqib Khan                     std::make_unique<phosphor::software::manager::Delete>(
260ee13e831SSaqib Khan                         bus, path, *versionPtr);
261ee13e831SSaqib Khan             }
2622285fe0fSAdriana Kobylak             versions.insert(std::make_pair(id, std::move(versionPtr)));
263ee590c74SAdriana Kobylak 
2641eef62deSSaqib Khan             // Create Activation instance for this version.
265ee13e831SSaqib Khan             activations.insert(std::make_pair(
2662285fe0fSAdriana Kobylak                 id, std::make_unique<Activation>(
2672285fe0fSAdriana Kobylak                         bus, path, *this, id, activationState, associations)));
2681eef62deSSaqib Khan 
269269bff30SLei YU             // If Active, create RedundancyPriority instance for this
270269bff30SLei YU             // version.
2711eef62deSSaqib Khan             if (activationState == server::Activation::Activations::Active)
2721eef62deSSaqib Khan             {
2731eef62deSSaqib Khan                 uint8_t priority = std::numeric_limits<uint8_t>::max();
274687e75e2SAdriana Kobylak                 if (!restorePriority(id, priority))
2751eef62deSSaqib Khan                 {
276ee590c74SAdriana Kobylak                     if (isVersionFunctional)
277ee590c74SAdriana Kobylak                     {
278ee590c74SAdriana Kobylak                         priority = 0;
279ee590c74SAdriana Kobylak                     }
280ee590c74SAdriana Kobylak                     else
281ee590c74SAdriana Kobylak                     {
2821eef62deSSaqib Khan                         log<level::ERR>("Unable to restore priority from file.",
283596466b8SAdriana Kobylak                                         entry("VERSIONID=%s", id.c_str()));
2841eef62deSSaqib Khan                     }
285ee590c74SAdriana Kobylak                 }
2861eef62deSSaqib Khan                 activations.find(id)->second->redundancyPriority =
2871eef62deSSaqib Khan                     std::make_unique<RedundancyPriority>(
2882285fe0fSAdriana Kobylak                         bus, path, *(activations.find(id)->second), priority,
289b77551cdSAdriana Kobylak                         false);
2901eef62deSSaqib Khan             }
2911eef62deSSaqib Khan         }
2921eef62deSSaqib Khan     }
293dcbfa04aSSaqib Khan 
294716cd78dSAdriana Kobylak     // If there are no bmc versions mounted under MEDIA_DIR, then read the
295716cd78dSAdriana Kobylak     // /etc/os-release and create rofs-<versionId> under MEDIA_DIR, then call
296716cd78dSAdriana Kobylak     // again processBMCImage() to create the D-Bus interface for it.
297dcbfa04aSSaqib Khan     if (activations.size() == 0)
298dcbfa04aSSaqib Khan     {
299d16bcbd5SGunnar Mills         auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE);
300dcbfa04aSSaqib Khan         auto id = phosphor::software::manager::Version::getId(version);
301dcbfa04aSSaqib Khan         auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/";
302dcbfa04aSSaqib Khan         try
303dcbfa04aSSaqib Khan         {
304dcbfa04aSSaqib Khan             if (!fs::is_directory(versionFileDir))
305dcbfa04aSSaqib Khan             {
306dcbfa04aSSaqib Khan                 fs::create_directories(versionFileDir);
307dcbfa04aSSaqib Khan             }
308dcbfa04aSSaqib Khan             auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE;
309dcbfa04aSSaqib Khan             fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath);
310dcbfa04aSSaqib Khan             ItemUpdater::processBMCImage();
311dcbfa04aSSaqib Khan         }
312dcbfa04aSSaqib Khan         catch (const std::exception& e)
313dcbfa04aSSaqib Khan         {
314dcbfa04aSSaqib Khan             log<level::ERR>(e.what());
315dcbfa04aSSaqib Khan         }
316dcbfa04aSSaqib Khan     }
317eaa1ee05SEddie James 
318eaa1ee05SEddie James     mirrorUbootToAlt();
319ba239881SSaqib Khan     return;
320ba239881SSaqib Khan }
321ba239881SSaqib Khan 
3223526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId)
3233526ef73SLeonel Gonzalez {
3246d873715SEddie James     // Find entry in versions map
3256d873715SEddie James     auto it = versions.find(entryId);
3266d873715SEddie James     if (it != versions.end())
3276d873715SEddie James     {
3280f88b5afSLei YU         if (it->second->isFunctional() && ACTIVE_BMC_MAX_ALLOWED > 1)
3296d873715SEddie James         {
3302285fe0fSAdriana Kobylak             log<level::ERR>("Error: Version is currently running on the BMC. "
3312285fe0fSAdriana Kobylak                             "Unable to remove.",
3322285fe0fSAdriana Kobylak                             entry("VERSIONID=%s", entryId.c_str()));
3336d873715SEddie James             return;
3346d873715SEddie James         }
335*d1a55adcSAdriana Kobylak     }
3366d873715SEddie James 
337*d1a55adcSAdriana Kobylak     // First call resetUbootEnvVars() so that the BMC points to a valid image to
338*d1a55adcSAdriana Kobylak     // boot from. If resetUbootEnvVars() is called after the image is actually
339*d1a55adcSAdriana Kobylak     // deleted from the BMC flash, there'd be a time window where the BMC would
340*d1a55adcSAdriana Kobylak     // be pointing to a non-existent image to boot from.
341*d1a55adcSAdriana Kobylak     // Need to remove the entries from the activations map before that call so
342*d1a55adcSAdriana Kobylak     // that resetUbootEnvVars() doesn't use the version to be deleted.
343*d1a55adcSAdriana Kobylak     auto iteratorActivations = activations.find(entryId);
344*d1a55adcSAdriana Kobylak     if (iteratorActivations == activations.end())
345*d1a55adcSAdriana Kobylak     {
346*d1a55adcSAdriana Kobylak         log<level::ERR>("Error: Failed to find version in item updater "
347*d1a55adcSAdriana Kobylak                         "activations map. Unable to remove.",
348*d1a55adcSAdriana Kobylak                         entry("VERSIONID=%s", entryId.c_str()));
349*d1a55adcSAdriana Kobylak     }
350*d1a55adcSAdriana Kobylak     else
351*d1a55adcSAdriana Kobylak     {
352*d1a55adcSAdriana Kobylak         removeAssociations(iteratorActivations->second->path);
353*d1a55adcSAdriana Kobylak         this->activations.erase(entryId);
354*d1a55adcSAdriana Kobylak     }
355*d1a55adcSAdriana Kobylak     ItemUpdater::resetUbootEnvVars();
356*d1a55adcSAdriana Kobylak 
357*d1a55adcSAdriana Kobylak     if (it != versions.end())
358*d1a55adcSAdriana Kobylak     {
3596d873715SEddie James         // Delete ReadOnly partitions if it's not active
3603526ef73SLeonel Gonzalez         removeReadOnlyPartition(entryId);
361687e75e2SAdriana Kobylak         removePersistDataDirectory(entryId);
362ee13e831SSaqib Khan 
363ee13e831SSaqib Khan         // Removing entry in versions map
364ee13e831SSaqib Khan         this->versions.erase(entryId);
3656d873715SEddie James     }
3666d873715SEddie James     else
3676d873715SEddie James     {
3686d873715SEddie James         // Delete ReadOnly partitions even if we can't find the version
3696d873715SEddie James         removeReadOnlyPartition(entryId);
370687e75e2SAdriana Kobylak         removePersistDataDirectory(entryId);
3716d873715SEddie James 
3722285fe0fSAdriana Kobylak         log<level::ERR>("Error: Failed to find version in item updater "
3732285fe0fSAdriana Kobylak                         "versions map. Unable to remove.",
3742285fe0fSAdriana Kobylak                         entry("VERSIONID=%s", entryId.c_str()));
3756d873715SEddie James     }
3761eef62deSSaqib Khan 
37756aaf454SLei YU     helper.clearEntry(entryId);
3783526ef73SLeonel Gonzalez 
379ee13e831SSaqib Khan     return;
3803526ef73SLeonel Gonzalez }
3813526ef73SLeonel Gonzalez 
382bc1bf3afSMichael Tritz void ItemUpdater::deleteAll()
383bc1bf3afSMichael Tritz {
38483cd21fbSAdriana Kobylak     std::vector<std::string> deletableVersions;
38583cd21fbSAdriana Kobylak 
386bc1bf3afSMichael Tritz     for (const auto& versionIt : versions)
387bc1bf3afSMichael Tritz     {
388bc1bf3afSMichael Tritz         if (!versionIt.second->isFunctional())
389bc1bf3afSMichael Tritz         {
39083cd21fbSAdriana Kobylak             deletableVersions.push_back(versionIt.first);
391bc1bf3afSMichael Tritz         }
392bc1bf3afSMichael Tritz     }
393bc1bf3afSMichael Tritz 
39483cd21fbSAdriana Kobylak     for (const auto& deletableIt : deletableVersions)
39583cd21fbSAdriana Kobylak     {
39683cd21fbSAdriana Kobylak         ItemUpdater::erase(deletableIt);
39783cd21fbSAdriana Kobylak     }
39883cd21fbSAdriana Kobylak 
39956aaf454SLei YU     helper.cleanup();
400bc1bf3afSMichael Tritz }
401bc1bf3afSMichael Tritz 
4022285fe0fSAdriana Kobylak ItemUpdater::ActivationStatus
4032285fe0fSAdriana Kobylak     ItemUpdater::validateSquashFSImage(const std::string& filePath)
40435e83f3eSSaqib Khan {
405b1cfdf99SMichael Tritz     bool invalid = false;
40635e83f3eSSaqib Khan 
407b1cfdf99SMichael Tritz     for (auto& bmcImage : bmcImages)
408b1cfdf99SMichael Tritz     {
40919177d3eSSaqib Khan         fs::path file(filePath);
41035e83f3eSSaqib Khan         file /= bmcImage;
41135e83f3eSSaqib Khan         std::ifstream efile(file.c_str());
412b1cfdf99SMichael Tritz         if (efile.good() != 1)
41335e83f3eSSaqib Khan         {
414b1cfdf99SMichael Tritz             log<level::ERR>("Failed to find the BMC image.",
415b1cfdf99SMichael Tritz                             entry("IMAGE=%s", bmcImage.c_str()));
416b1cfdf99SMichael Tritz             invalid = true;
41735e83f3eSSaqib Khan         }
418b1cfdf99SMichael Tritz     }
419b1cfdf99SMichael Tritz 
420b1cfdf99SMichael Tritz     if (invalid)
42135e83f3eSSaqib Khan     {
42235e83f3eSSaqib Khan         return ItemUpdater::ActivationStatus::invalid;
42335e83f3eSSaqib Khan     }
424b1cfdf99SMichael Tritz 
425b1cfdf99SMichael Tritz     return ItemUpdater::ActivationStatus::ready;
42635e83f3eSSaqib Khan }
42735e83f3eSSaqib Khan 
428bbcb7be1SAdriana Kobylak void ItemUpdater::savePriority(const std::string& versionId, uint8_t value)
429bbcb7be1SAdriana Kobylak {
430687e75e2SAdriana Kobylak     storePriority(versionId, value);
431bbcb7be1SAdriana Kobylak     helper.setEntry(versionId, value);
432bbcb7be1SAdriana Kobylak }
433bbcb7be1SAdriana Kobylak 
434b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
4354c1aec09SSaqib Khan {
436b77551cdSAdriana Kobylak     std::map<std::string, uint8_t> priorityMap;
437b77551cdSAdriana Kobylak 
438b77551cdSAdriana Kobylak     // Insert the requested version and priority, it may not exist yet.
439b77551cdSAdriana Kobylak     priorityMap.insert(std::make_pair(versionId, value));
440b77551cdSAdriana Kobylak 
4414c1aec09SSaqib Khan     for (const auto& intf : activations)
4424c1aec09SSaqib Khan     {
4434c1aec09SSaqib Khan         if (intf.second->redundancyPriority)
4444c1aec09SSaqib Khan         {
445b77551cdSAdriana Kobylak             priorityMap.insert(std::make_pair(
4462285fe0fSAdriana Kobylak                 intf.first, intf.second->redundancyPriority.get()->priority()));
447b77551cdSAdriana Kobylak         }
448b77551cdSAdriana Kobylak     }
449b77551cdSAdriana Kobylak 
450b77551cdSAdriana Kobylak     // Lambda function to compare 2 priority values, use <= to allow duplicates
4512285fe0fSAdriana Kobylak     typedef std::function<bool(std::pair<std::string, uint8_t>,
4522285fe0fSAdriana Kobylak                                std::pair<std::string, uint8_t>)>
4532285fe0fSAdriana Kobylak         cmpPriority;
4542285fe0fSAdriana Kobylak     cmpPriority cmpPriorityFunc =
4552285fe0fSAdriana Kobylak         [](std::pair<std::string, uint8_t> priority1,
4562285fe0fSAdriana Kobylak            std::pair<std::string, uint8_t> priority2) {
457b77551cdSAdriana Kobylak             return priority1.second <= priority2.second;
458b77551cdSAdriana Kobylak         };
459b77551cdSAdriana Kobylak 
460b77551cdSAdriana Kobylak     // Sort versions by ascending priority
461b77551cdSAdriana Kobylak     std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet(
462b77551cdSAdriana Kobylak         priorityMap.begin(), priorityMap.end(), cmpPriorityFunc);
463b77551cdSAdriana Kobylak 
464b77551cdSAdriana Kobylak     auto freePriorityValue = value;
465b77551cdSAdriana Kobylak     for (auto& element : prioritySet)
466b77551cdSAdriana Kobylak     {
467b77551cdSAdriana Kobylak         if (element.first == versionId)
468b77551cdSAdriana Kobylak         {
469b77551cdSAdriana Kobylak             continue;
470b77551cdSAdriana Kobylak         }
471b77551cdSAdriana Kobylak         if (element.second == freePriorityValue)
472b77551cdSAdriana Kobylak         {
473b77551cdSAdriana Kobylak             ++freePriorityValue;
474b77551cdSAdriana Kobylak             auto it = activations.find(element.first);
475b77551cdSAdriana Kobylak             it->second->redundancyPriority.get()->sdbusPriority(
476b77551cdSAdriana Kobylak                 freePriorityValue);
4774c1aec09SSaqib Khan         }
4784c1aec09SSaqib Khan     }
479b77551cdSAdriana Kobylak 
480b77551cdSAdriana Kobylak     auto lowestVersion = prioritySet.begin()->first;
481b77551cdSAdriana Kobylak     if (value == prioritySet.begin()->second)
482b77551cdSAdriana Kobylak     {
483b77551cdSAdriana Kobylak         lowestVersion = versionId;
4844c1aec09SSaqib Khan     }
485b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestVersion);
4864c1aec09SSaqib Khan }
4874c1aec09SSaqib Khan 
48837a59043SMichael Tritz void ItemUpdater::reset()
48937a59043SMichael Tritz {
49060f5ccfdSGunnar Mills     constexpr auto setFactoryResetWait = std::chrono::seconds(3);
49156aaf454SLei YU     helper.factoryReset();
49237a59043SMichael Tritz 
49360f5ccfdSGunnar Mills     // Need to wait for env variables to complete, otherwise an immediate reboot
49460f5ccfdSGunnar Mills     // will not factory reset.
49560f5ccfdSGunnar Mills     std::this_thread::sleep_for(setFactoryResetWait);
49660f5ccfdSGunnar Mills 
49737a59043SMichael Tritz     log<level::INFO>("BMC factory reset will take effect upon reboot.");
49837a59043SMichael Tritz }
49937a59043SMichael Tritz 
5003526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId)
5013526ef73SLeonel Gonzalez {
50256aaf454SLei YU     helper.removeVersion(versionId);
5033526ef73SLeonel Gonzalez }
5043526ef73SLeonel Gonzalez 
5050129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value)
5060129d926SMichael Tritz {
5070129d926SMichael Tritz     // enabling field mode is intended to be one way: false -> true
5080129d926SMichael Tritz     if (value && !control::FieldMode::fieldModeEnabled())
5090129d926SMichael Tritz     {
5100129d926SMichael Tritz         control::FieldMode::fieldModeEnabled(value);
5110129d926SMichael Tritz 
51222848eceSAdriana Kobylak         auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
51322848eceSAdriana Kobylak                                           SYSTEMD_INTERFACE, "StartUnit");
51422848eceSAdriana Kobylak         method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service",
51522848eceSAdriana Kobylak                       "replace");
51622848eceSAdriana Kobylak         bus.call_noreply(method);
51722848eceSAdriana Kobylak 
51822848eceSAdriana Kobylak         method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
51922848eceSAdriana Kobylak                                      SYSTEMD_INTERFACE, "StopUnit");
52022848eceSAdriana Kobylak         method.append("usr-local.mount", "replace");
52122848eceSAdriana Kobylak         bus.call_noreply(method);
52222848eceSAdriana Kobylak 
52322848eceSAdriana Kobylak         std::vector<std::string> usrLocal = {"usr-local.mount"};
52422848eceSAdriana Kobylak 
52522848eceSAdriana Kobylak         method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
52622848eceSAdriana Kobylak                                      SYSTEMD_INTERFACE, "MaskUnitFiles");
52722848eceSAdriana Kobylak         method.append(usrLocal, false, true);
52822848eceSAdriana Kobylak         bus.call_noreply(method);
5290129d926SMichael Tritz     }
530d5b8f75cSAdriana Kobylak     else if (!value && control::FieldMode::fieldModeEnabled())
531d5b8f75cSAdriana Kobylak     {
532d5b8f75cSAdriana Kobylak         elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON(
533d5b8f75cSAdriana Kobylak             "FieldMode is not allowed to be cleared"));
534d5b8f75cSAdriana Kobylak     }
5350129d926SMichael Tritz 
5360129d926SMichael Tritz     return control::FieldMode::fieldModeEnabled();
5370129d926SMichael Tritz }
5380129d926SMichael Tritz 
5390129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus()
5400129d926SMichael Tritz {
541ff0b421dSMichael Tritz     std::ifstream input("/dev/mtd/u-boot-env");
5420129d926SMichael Tritz     std::string envVar;
5430129d926SMichael Tritz     std::getline(input, envVar);
5440129d926SMichael Tritz 
5450129d926SMichael Tritz     if (envVar.find("fieldmode=true") != std::string::npos)
5460129d926SMichael Tritz     {
5470129d926SMichael Tritz         ItemUpdater::fieldModeEnabled(true);
5480129d926SMichael Tritz     }
5490129d926SMichael Tritz }
5500129d926SMichael Tritz 
551b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath()
552b60add1eSGunnar Mills {
553b60add1eSGunnar Mills     auto depth = 0;
5542285fe0fSAdriana Kobylak     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
5552285fe0fSAdriana Kobylak                                           MAPPER_INTERFACE, "GetSubTreePaths");
556b60add1eSGunnar Mills 
5571254c628SAdriana Kobylak     mapperCall.append(INVENTORY_PATH);
558b60add1eSGunnar Mills     mapperCall.append(depth);
5591254c628SAdriana Kobylak     std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE};
560b60add1eSGunnar Mills     mapperCall.append(filter);
561b60add1eSGunnar Mills 
56287c78173SEd Tanous     try
563b60add1eSGunnar Mills     {
56487c78173SEd Tanous         auto response = bus.call(mapperCall);
565b60add1eSGunnar Mills 
566b60add1eSGunnar Mills         using ObjectPaths = std::vector<std::string>;
567b60add1eSGunnar Mills         ObjectPaths result;
568b60add1eSGunnar Mills         response.read(result);
569b60add1eSGunnar Mills 
5701254c628SAdriana Kobylak         if (!result.empty())
571b60add1eSGunnar Mills         {
5721254c628SAdriana Kobylak             bmcInventoryPath = result.front();
573b60add1eSGunnar Mills         }
57487c78173SEd Tanous     }
57587c78173SEd Tanous     catch (const sdbusplus::exception::SdBusError& e)
57687c78173SEd Tanous     {
57787c78173SEd Tanous         log<level::ERR>("Error in mapper GetSubTreePath");
57887c78173SEd Tanous         return;
57987c78173SEd Tanous     }
580b60add1eSGunnar Mills 
581b60add1eSGunnar Mills     return;
582b60add1eSGunnar Mills }
583b60add1eSGunnar Mills 
584f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path)
585ded875dcSGunnar Mills {
5862285fe0fSAdriana Kobylak     assocs.emplace_back(
5872285fe0fSAdriana Kobylak         std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
588ded875dcSGunnar Mills     associations(assocs);
589ded875dcSGunnar Mills }
590ded875dcSGunnar Mills 
59188e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path)
59288e8a325SGunnar Mills {
59388e8a325SGunnar Mills     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
5942285fe0fSAdriana Kobylak                                         FUNCTIONAL_REV_ASSOCIATION, path));
59588e8a325SGunnar Mills     associations(assocs);
59688e8a325SGunnar Mills }
59788e8a325SGunnar Mills 
598bbebec79SAppaRao Puli void ItemUpdater::createUpdateableAssociation(const std::string& path)
599bbebec79SAppaRao Puli {
600bbebec79SAppaRao Puli     assocs.emplace_back(std::make_tuple(UPDATEABLE_FWD_ASSOCIATION,
601bbebec79SAppaRao Puli                                         UPDATEABLE_REV_ASSOCIATION, path));
602bbebec79SAppaRao Puli     associations(assocs);
603bbebec79SAppaRao Puli }
604bbebec79SAppaRao Puli 
605991af7ecSAdriana Kobylak void ItemUpdater::removeAssociations(const std::string& path)
606ded875dcSGunnar Mills {
607ded875dcSGunnar Mills     for (auto iter = assocs.begin(); iter != assocs.end();)
608ded875dcSGunnar Mills     {
609991af7ecSAdriana Kobylak         if ((std::get<2>(*iter)).compare(path) == 0)
610ded875dcSGunnar Mills         {
611ded875dcSGunnar Mills             iter = assocs.erase(iter);
612ded875dcSGunnar Mills             associations(assocs);
613ded875dcSGunnar Mills         }
614ded875dcSGunnar Mills         else
615ded875dcSGunnar Mills         {
616ded875dcSGunnar Mills             ++iter;
617ded875dcSGunnar Mills         }
618ded875dcSGunnar Mills     }
619ded875dcSGunnar Mills }
620ded875dcSGunnar Mills 
621b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value)
622b9da6634SSaqib Khan {
623b9da6634SSaqib Khan     for (const auto& intf : activations)
624b9da6634SSaqib Khan     {
625b9da6634SSaqib Khan         if (intf.second->redundancyPriority)
626b9da6634SSaqib Khan         {
627b9da6634SSaqib Khan             if (intf.second->redundancyPriority.get()->priority() < value)
628b9da6634SSaqib Khan             {
629b9da6634SSaqib Khan                 return false;
630b9da6634SSaqib Khan             }
631b9da6634SSaqib Khan         }
632b9da6634SSaqib Khan     }
633b9da6634SSaqib Khan     return true;
634b9da6634SSaqib Khan }
635b9da6634SSaqib Khan 
636b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId)
637b77551cdSAdriana Kobylak {
63856aaf454SLei YU     helper.updateUbootVersionId(versionId);
639b77551cdSAdriana Kobylak }
640b77551cdSAdriana Kobylak 
64149446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars()
64249446ae9SSaqib Khan {
64349446ae9SSaqib Khan     decltype(activations.begin()->second->redundancyPriority.get()->priority())
64449446ae9SSaqib Khan         lowestPriority = std::numeric_limits<uint8_t>::max();
64549446ae9SSaqib Khan     decltype(activations.begin()->second->versionId) lowestPriorityVersion;
64649446ae9SSaqib Khan     for (const auto& intf : activations)
64749446ae9SSaqib Khan     {
64849446ae9SSaqib Khan         if (!intf.second->redundancyPriority.get())
64949446ae9SSaqib Khan         {
65049446ae9SSaqib Khan             // Skip this version if the redundancyPriority is not initialized.
65149446ae9SSaqib Khan             continue;
65249446ae9SSaqib Khan         }
65349446ae9SSaqib Khan 
6542285fe0fSAdriana Kobylak         if (intf.second->redundancyPriority.get()->priority() <= lowestPriority)
65549446ae9SSaqib Khan         {
65649446ae9SSaqib Khan             lowestPriority = intf.second->redundancyPriority.get()->priority();
65749446ae9SSaqib Khan             lowestPriorityVersion = intf.second->versionId;
65849446ae9SSaqib Khan         }
65949446ae9SSaqib Khan     }
66049446ae9SSaqib Khan 
661f0382c35SSaqib Khan     // Update the U-boot environment variable to point to the lowest priority
662b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestPriorityVersion);
66349446ae9SSaqib Khan }
66449446ae9SSaqib Khan 
665a6963590SAdriana Kobylak void ItemUpdater::freeSpace(Activation& caller)
666204e1e74SAdriana Kobylak {
667204e1e74SAdriana Kobylak     //  Versions with the highest priority in front
668204e1e74SAdriana Kobylak     std::priority_queue<std::pair<int, std::string>,
669204e1e74SAdriana Kobylak                         std::vector<std::pair<int, std::string>>,
6702285fe0fSAdriana Kobylak                         std::less<std::pair<int, std::string>>>
6712285fe0fSAdriana Kobylak         versionsPQ;
672204e1e74SAdriana Kobylak 
673204e1e74SAdriana Kobylak     std::size_t count = 0;
674204e1e74SAdriana Kobylak     for (const auto& iter : activations)
675204e1e74SAdriana Kobylak     {
676204e1e74SAdriana Kobylak         if ((iter.second.get()->activation() ==
677204e1e74SAdriana Kobylak              server::Activation::Activations::Active) ||
678204e1e74SAdriana Kobylak             (iter.second.get()->activation() ==
679204e1e74SAdriana Kobylak              server::Activation::Activations::Failed))
680204e1e74SAdriana Kobylak         {
681204e1e74SAdriana Kobylak             count++;
682204e1e74SAdriana Kobylak             // Don't put the functional version on the queue since we can't
683204e1e74SAdriana Kobylak             // remove the "running" BMC version.
6840f88b5afSLei YU             // If ACTIVE_BMC_MAX_ALLOWED <= 1, there is only one active BMC,
6850f88b5afSLei YU             // so remove functional version as well.
686a6963590SAdriana Kobylak             // Don't delete the the Activation object that called this function.
687a6963590SAdriana Kobylak             if ((versions.find(iter.second->versionId)
688a6963590SAdriana Kobylak                      ->second->isFunctional() &&
689a6963590SAdriana Kobylak                  ACTIVE_BMC_MAX_ALLOWED > 1) ||
690a6963590SAdriana Kobylak                 (iter.second->versionId == caller.versionId))
691204e1e74SAdriana Kobylak             {
692204e1e74SAdriana Kobylak                 continue;
693204e1e74SAdriana Kobylak             }
694a6963590SAdriana Kobylak 
695a6963590SAdriana Kobylak             // Failed activations don't have priority, assign them a large value
696a6963590SAdriana Kobylak             // for sorting purposes.
697a6963590SAdriana Kobylak             auto priority = 999;
698a6963590SAdriana Kobylak             if (iter.second.get()->activation() ==
699a6963590SAdriana Kobylak                 server::Activation::Activations::Active)
700a6963590SAdriana Kobylak             {
701a6963590SAdriana Kobylak                 priority = iter.second->redundancyPriority.get()->priority();
702a6963590SAdriana Kobylak             }
703a6963590SAdriana Kobylak 
704a6963590SAdriana Kobylak             versionsPQ.push(std::make_pair(priority, iter.second->versionId));
705204e1e74SAdriana Kobylak         }
706204e1e74SAdriana Kobylak     }
707204e1e74SAdriana Kobylak 
708204e1e74SAdriana Kobylak     // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1,
709204e1e74SAdriana Kobylak     // remove the highest priority one(s).
710204e1e74SAdriana Kobylak     while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty()))
711204e1e74SAdriana Kobylak     {
712204e1e74SAdriana Kobylak         erase(versionsPQ.top().second);
713204e1e74SAdriana Kobylak         versionsPQ.pop();
714204e1e74SAdriana Kobylak         count--;
715204e1e74SAdriana Kobylak     }
716204e1e74SAdriana Kobylak }
717204e1e74SAdriana Kobylak 
718eaa1ee05SEddie James void ItemUpdater::mirrorUbootToAlt()
719eaa1ee05SEddie James {
72056aaf454SLei YU     helper.mirrorAlt();
721eaa1ee05SEddie James }
722eaa1ee05SEddie James 
723ec1b41c4SGunnar Mills } // namespace updater
724ec1b41c4SGunnar Mills } // namespace software
725ec1b41c4SGunnar Mills } // namespace phosphor
726