135e83f3eSSaqib Khan #include <fstream>
2204e1e74SAdriana Kobylak #include <queue>
3b77551cdSAdriana Kobylak #include <set>
4ec1b41c4SGunnar Mills #include <string>
52ce7da29SGunnar Mills #include <phosphor-logging/log.hpp>
6dcbfa04aSSaqib Khan #include <phosphor-logging/elog.hpp>
7dcbfa04aSSaqib Khan #include <elog-errors.hpp>
8dcbfa04aSSaqib Khan #include <xyz/openbmc_project/Software/Version/error.hpp>
9ec1b41c4SGunnar Mills #include "config.h"
102ce7da29SGunnar Mills #include "item_updater.hpp"
112ce7da29SGunnar Mills #include "xyz/openbmc_project/Software/Version/server.hpp"
1235e83f3eSSaqib Khan #include <experimental/filesystem>
13705f1bfcSSaqib Khan #include "version.hpp"
145d532675SSaqib Khan #include "serialize.hpp"
15*1be8d500SLei YU #include "images.hpp"
16ec1b41c4SGunnar Mills 
17ec1b41c4SGunnar Mills namespace phosphor
18ec1b41c4SGunnar Mills {
19ec1b41c4SGunnar Mills namespace software
20ec1b41c4SGunnar Mills {
21ec1b41c4SGunnar Mills namespace updater
22ec1b41c4SGunnar Mills {
23ec1b41c4SGunnar Mills 
242ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class
252ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server;
260129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server;
272ce7da29SGunnar Mills 
282ce7da29SGunnar Mills using namespace phosphor::logging;
29dcbfa04aSSaqib Khan using namespace sdbusplus::xyz::openbmc_project::Software::Version::Error;
302ab9b109SJayanth Othayoth using namespace phosphor::software::image;
3135e83f3eSSaqib Khan namespace fs = std::experimental::filesystem;
3235e83f3eSSaqib Khan 
33e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg)
34ec1b41c4SGunnar Mills {
3584a0e693SSaqib Khan 
3684a0e693SSaqib Khan     using SVersion = server::Version;
3784a0e693SSaqib Khan     using VersionPurpose = SVersion::VersionPurpose;
389a782243SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
3984a0e693SSaqib Khan     namespace mesg = sdbusplus::message;
4084a0e693SSaqib Khan     namespace variant_ns = mesg::variant_ns;
4184a0e693SSaqib Khan 
4284a0e693SSaqib Khan     mesg::object_path objPath;
4384a0e693SSaqib Khan     auto purpose = VersionPurpose::Unknown;
44705f1bfcSSaqib Khan     std::string version;
452285fe0fSAdriana Kobylak     std::map<std::string, std::map<std::string, mesg::variant<std::string>>>
462285fe0fSAdriana Kobylak         interfaces;
47e75d10f5SPatrick Williams     msg.read(objPath, interfaces);
482ce7da29SGunnar Mills     std::string path(std::move(objPath));
4919177d3eSSaqib Khan     std::string filePath;
502ce7da29SGunnar Mills 
512ce7da29SGunnar Mills     for (const auto& intf : interfaces)
522ce7da29SGunnar Mills     {
53705f1bfcSSaqib Khan         if (intf.first == VERSION_IFACE)
542ce7da29SGunnar Mills         {
552ce7da29SGunnar Mills             for (const auto& property : intf.second)
562ce7da29SGunnar Mills             {
57705f1bfcSSaqib Khan                 if (property.first == "Purpose")
582ce7da29SGunnar Mills                 {
5984a0e693SSaqib Khan                     auto value = SVersion::convertVersionPurposeFromString(
6084a0e693SSaqib Khan                         variant_ns::get<std::string>(property.second));
6184a0e693SSaqib Khan                     if (value == VersionPurpose::BMC ||
6284a0e693SSaqib Khan                         value == VersionPurpose::System)
6384a0e693SSaqib Khan                     {
6484a0e693SSaqib Khan                         purpose = value;
6584a0e693SSaqib Khan                     }
66705f1bfcSSaqib Khan                 }
67705f1bfcSSaqib Khan                 else if (property.first == "Version")
68705f1bfcSSaqib Khan                 {
6984a0e693SSaqib Khan                     version = variant_ns::get<std::string>(property.second);
70705f1bfcSSaqib Khan                 }
71705f1bfcSSaqib Khan             }
72705f1bfcSSaqib Khan         }
7319177d3eSSaqib Khan         else if (intf.first == FILEPATH_IFACE)
7419177d3eSSaqib Khan         {
7519177d3eSSaqib Khan             for (const auto& property : intf.second)
7619177d3eSSaqib Khan             {
7719177d3eSSaqib Khan                 if (property.first == "Path")
7819177d3eSSaqib Khan                 {
7984a0e693SSaqib Khan                     filePath = variant_ns::get<std::string>(property.second);
8019177d3eSSaqib Khan                 }
8119177d3eSSaqib Khan             }
8219177d3eSSaqib Khan         }
83705f1bfcSSaqib Khan     }
842285fe0fSAdriana Kobylak     if (version.empty() || filePath.empty() ||
8584a0e693SSaqib Khan         purpose == VersionPurpose::Unknown)
862ce7da29SGunnar Mills     {
87e75d10f5SPatrick Williams         return;
882ce7da29SGunnar Mills     }
892ce7da29SGunnar Mills 
902ce7da29SGunnar Mills     // Version id is the last item in the path
912ce7da29SGunnar Mills     auto pos = path.rfind("/");
922ce7da29SGunnar Mills     if (pos == std::string::npos)
932ce7da29SGunnar Mills     {
942ce7da29SGunnar Mills         log<level::ERR>("No version id found in object path",
95596466b8SAdriana Kobylak                         entry("OBJPATH=%s", path.c_str()));
96e75d10f5SPatrick Williams         return;
972ce7da29SGunnar Mills     }
982ce7da29SGunnar Mills 
992ce7da29SGunnar Mills     auto versionId = path.substr(pos + 1);
1002ce7da29SGunnar Mills 
101e75d10f5SPatrick Williams     if (activations.find(versionId) == activations.end())
1022ce7da29SGunnar Mills     {
10335e83f3eSSaqib Khan         // Determine the Activation state by processing the given image dir.
10435e83f3eSSaqib Khan         auto activationState = server::Activation::Activations::Invalid;
1059a782243SGunnar Mills         ItemUpdater::ActivationStatus result =
1069a782243SGunnar Mills             ItemUpdater::validateSquashFSImage(filePath);
10743b25cdeSGunnar Mills         AssociationList associations = {};
10843b25cdeSGunnar Mills 
10935e83f3eSSaqib Khan         if (result == ItemUpdater::ActivationStatus::ready)
11035e83f3eSSaqib Khan         {
11135e83f3eSSaqib Khan             activationState = server::Activation::Activations::Ready;
112b60add1eSGunnar Mills             // Create an association to the BMC inventory item
1132285fe0fSAdriana Kobylak             associations.emplace_back(
1142285fe0fSAdriana Kobylak                 std::make_tuple(ACTIVATION_FWD_ASSOCIATION,
1152285fe0fSAdriana Kobylak                                 ACTIVATION_REV_ASSOCIATION, bmcInventoryPath));
11643b25cdeSGunnar Mills         }
117b60add1eSGunnar Mills 
118ee13e831SSaqib Khan         activations.insert(std::make_pair(
119ee13e831SSaqib Khan             versionId,
1202285fe0fSAdriana Kobylak             std::make_unique<Activation>(bus, path, *this, versionId,
1212285fe0fSAdriana Kobylak                                          activationState, associations)));
1224254beceSMichael Tritz 
123ee13e831SSaqib Khan         auto versionPtr = std::make_unique<VersionClass>(
1242285fe0fSAdriana Kobylak             bus, path, version, purpose, filePath,
1252285fe0fSAdriana Kobylak             std::bind(&ItemUpdater::erase, this, std::placeholders::_1));
126ee13e831SSaqib Khan         versionPtr->deleteObject =
1272285fe0fSAdriana Kobylak             std::make_unique<phosphor::software::manager::Delete>(bus, path,
1282285fe0fSAdriana Kobylak                                                                   *versionPtr);
129ee13e831SSaqib Khan         versions.insert(std::make_pair(versionId, std::move(versionPtr)));
1302ce7da29SGunnar Mills     }
131e75d10f5SPatrick Williams     return;
132ec1b41c4SGunnar Mills }
133ec1b41c4SGunnar Mills 
134ba239881SSaqib Khan void ItemUpdater::processBMCImage()
135ba239881SSaqib Khan {
13688e8a325SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
13788e8a325SGunnar Mills     // Read os-release from /etc/ to get the functional BMC version
13888e8a325SGunnar Mills     auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE);
13988e8a325SGunnar Mills 
1401eef62deSSaqib Khan     // Read os-release from folders under /media/ to get
1411eef62deSSaqib Khan     // BMC Software Versions.
1421eef62deSSaqib Khan     for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
1431eef62deSSaqib Khan     {
1441eef62deSSaqib Khan         auto activationState = server::Activation::Activations::Active;
1456fab70daSSaqib Khan         static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX);
1461eef62deSSaqib Khan 
1471eef62deSSaqib Khan         // Check if the BMC_RO_PREFIXis the prefix of the iter.path
1482285fe0fSAdriana Kobylak         if (0 ==
1492285fe0fSAdriana Kobylak             iter.path().native().compare(0, BMC_RO_PREFIX_LEN, BMC_ROFS_PREFIX))
1501eef62deSSaqib Khan         {
151021c365bSSaqib Khan             // The versionId is extracted from the path
152021c365bSSaqib Khan             // for example /media/ro-2a1022fe.
153021c365bSSaqib Khan             auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
1541eef62deSSaqib Khan             auto osRelease = iter.path() / OS_RELEASE_FILE;
1551eef62deSSaqib Khan             if (!fs::is_regular_file(osRelease))
1561eef62deSSaqib Khan             {
1572285fe0fSAdriana Kobylak                 log<level::ERR>(
1582285fe0fSAdriana Kobylak                     "Failed to read osRelease",
159596466b8SAdriana Kobylak                     entry("FILENAME=%s", osRelease.string().c_str()));
160021c365bSSaqib Khan                 ItemUpdater::erase(id);
161021c365bSSaqib Khan                 continue;
1621eef62deSSaqib Khan             }
16388e8a325SGunnar Mills             auto version = VersionClass::getBMCVersion(osRelease);
1641eef62deSSaqib Khan             if (version.empty())
1651eef62deSSaqib Khan             {
1662285fe0fSAdriana Kobylak                 log<level::ERR>(
1672285fe0fSAdriana Kobylak                     "Failed to read version from osRelease",
168596466b8SAdriana Kobylak                     entry("FILENAME=%s", osRelease.string().c_str()));
1691eef62deSSaqib Khan                 activationState = server::Activation::Activations::Invalid;
1701eef62deSSaqib Khan             }
171021c365bSSaqib Khan 
1721eef62deSSaqib Khan             auto purpose = server::Version::VersionPurpose::BMC;
1731eef62deSSaqib Khan             auto path = fs::path(SOFTWARE_OBJPATH) / id;
1741eef62deSSaqib Khan 
17588e8a325SGunnar Mills             // Create functional association if this is the functional version
17688e8a325SGunnar Mills             if (version.compare(functionalVersion) == 0)
17788e8a325SGunnar Mills             {
17888e8a325SGunnar Mills                 createFunctionalAssociation(path);
17988e8a325SGunnar Mills             }
18088e8a325SGunnar Mills 
18143b25cdeSGunnar Mills             AssociationList associations = {};
18243b25cdeSGunnar Mills 
18343b25cdeSGunnar Mills             if (activationState == server::Activation::Activations::Active)
18443b25cdeSGunnar Mills             {
18543b25cdeSGunnar Mills                 // Create an association to the BMC inventory item
18643b25cdeSGunnar Mills                 associations.emplace_back(std::make_tuple(
1872285fe0fSAdriana Kobylak                     ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
18843b25cdeSGunnar Mills                     bmcInventoryPath));
18943b25cdeSGunnar Mills 
19043b25cdeSGunnar Mills                 // Create an active association since this image is active
19143b25cdeSGunnar Mills                 createActiveAssociation(path);
19243b25cdeSGunnar Mills             }
19343b25cdeSGunnar Mills 
194ee590c74SAdriana Kobylak             // Create Version instance for this version.
195ee590c74SAdriana Kobylak             auto versionPtr = std::make_unique<VersionClass>(
1962285fe0fSAdriana Kobylak                 bus, path, version, purpose, "",
1972285fe0fSAdriana Kobylak                 std::bind(&ItemUpdater::erase, this, std::placeholders::_1));
198ee590c74SAdriana Kobylak             auto isVersionFunctional = versionPtr->isFunctional();
199ee13e831SSaqib Khan             if (!isVersionFunctional)
200ee13e831SSaqib Khan             {
201ee13e831SSaqib Khan                 versionPtr->deleteObject =
202ee13e831SSaqib Khan                     std::make_unique<phosphor::software::manager::Delete>(
203ee13e831SSaqib Khan                         bus, path, *versionPtr);
204ee13e831SSaqib Khan             }
2052285fe0fSAdriana Kobylak             versions.insert(std::make_pair(id, std::move(versionPtr)));
206ee590c74SAdriana Kobylak 
2071eef62deSSaqib Khan             // Create Activation instance for this version.
208ee13e831SSaqib Khan             activations.insert(std::make_pair(
2092285fe0fSAdriana Kobylak                 id, std::make_unique<Activation>(
2102285fe0fSAdriana Kobylak                         bus, path, *this, id, activationState, associations)));
2111eef62deSSaqib Khan 
2121eef62deSSaqib Khan             // If Active, create RedundancyPriority instance for this version.
2131eef62deSSaqib Khan             if (activationState == server::Activation::Activations::Active)
2141eef62deSSaqib Khan             {
2151eef62deSSaqib Khan                 uint8_t priority = std::numeric_limits<uint8_t>::max();
2161eef62deSSaqib Khan                 if (!restoreFromFile(id, priority))
2171eef62deSSaqib Khan                 {
218ee590c74SAdriana Kobylak                     if (isVersionFunctional)
219ee590c74SAdriana Kobylak                     {
220ee590c74SAdriana Kobylak                         priority = 0;
221ee590c74SAdriana Kobylak                     }
222ee590c74SAdriana Kobylak                     else
223ee590c74SAdriana Kobylak                     {
2241eef62deSSaqib Khan                         log<level::ERR>("Unable to restore priority from file.",
225596466b8SAdriana Kobylak                                         entry("VERSIONID=%s", id.c_str()));
2261eef62deSSaqib Khan                     }
227ee590c74SAdriana Kobylak                 }
2281eef62deSSaqib Khan                 activations.find(id)->second->redundancyPriority =
2291eef62deSSaqib Khan                     std::make_unique<RedundancyPriority>(
2302285fe0fSAdriana Kobylak                         bus, path, *(activations.find(id)->second), priority,
231b77551cdSAdriana Kobylak                         false);
2321eef62deSSaqib Khan             }
2331eef62deSSaqib Khan         }
2341eef62deSSaqib Khan     }
235dcbfa04aSSaqib Khan 
236dcbfa04aSSaqib Khan     // If there is no ubi volume for bmc version then read the /etc/os-release
237dcbfa04aSSaqib Khan     // and create rofs-<versionId> under /media
238dcbfa04aSSaqib Khan     if (activations.size() == 0)
239dcbfa04aSSaqib Khan     {
240d16bcbd5SGunnar Mills         auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE);
241dcbfa04aSSaqib Khan         auto id = phosphor::software::manager::Version::getId(version);
242dcbfa04aSSaqib Khan         auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/";
243dcbfa04aSSaqib Khan         try
244dcbfa04aSSaqib Khan         {
245dcbfa04aSSaqib Khan             if (!fs::is_directory(versionFileDir))
246dcbfa04aSSaqib Khan             {
247dcbfa04aSSaqib Khan                 fs::create_directories(versionFileDir);
248dcbfa04aSSaqib Khan             }
249dcbfa04aSSaqib Khan             auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE;
250dcbfa04aSSaqib Khan             fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath);
251dcbfa04aSSaqib Khan             ItemUpdater::processBMCImage();
252dcbfa04aSSaqib Khan         }
253dcbfa04aSSaqib Khan         catch (const std::exception& e)
254dcbfa04aSSaqib Khan         {
255dcbfa04aSSaqib Khan             log<level::ERR>(e.what());
256dcbfa04aSSaqib Khan         }
257dcbfa04aSSaqib Khan     }
258eaa1ee05SEddie James 
259eaa1ee05SEddie James     mirrorUbootToAlt();
260ba239881SSaqib Khan     return;
261ba239881SSaqib Khan }
262ba239881SSaqib Khan 
2633526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId)
2643526ef73SLeonel Gonzalez {
2656d873715SEddie James     // Find entry in versions map
2666d873715SEddie James     auto it = versions.find(entryId);
2676d873715SEddie James     if (it != versions.end())
2686d873715SEddie James     {
2696d873715SEddie James         if (it->second->isFunctional())
2706d873715SEddie James         {
2712285fe0fSAdriana Kobylak             log<level::ERR>("Error: Version is currently running on the BMC. "
2722285fe0fSAdriana Kobylak                             "Unable to remove.",
2732285fe0fSAdriana Kobylak                             entry("VERSIONID=%s", entryId.c_str()));
2746d873715SEddie James             return;
2756d873715SEddie James         }
2766d873715SEddie James 
2776d873715SEddie James         // Delete ReadOnly partitions if it's not active
2783526ef73SLeonel Gonzalez         removeReadOnlyPartition(entryId);
2791eef62deSSaqib Khan         removeFile(entryId);
280ee13e831SSaqib Khan 
281ee13e831SSaqib Khan         // Removing entry in versions map
282ee13e831SSaqib Khan         this->versions.erase(entryId);
2836d873715SEddie James     }
2846d873715SEddie James     else
2856d873715SEddie James     {
2866d873715SEddie James         // Delete ReadOnly partitions even if we can't find the version
2876d873715SEddie James         removeReadOnlyPartition(entryId);
2886d873715SEddie James         removeFile(entryId);
2896d873715SEddie James 
2902285fe0fSAdriana Kobylak         log<level::ERR>("Error: Failed to find version in item updater "
2912285fe0fSAdriana Kobylak                         "versions map. Unable to remove.",
2922285fe0fSAdriana Kobylak                         entry("VERSIONID=%s", entryId.c_str()));
2936d873715SEddie James     }
2941eef62deSSaqib Khan 
2951eef62deSSaqib Khan     // Remove the priority environment variable.
2961eef62deSSaqib Khan     auto serviceFile = "obmc-flash-bmc-setenv@" + entryId + ".service";
2972285fe0fSAdriana Kobylak     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
2982285fe0fSAdriana Kobylak                                       SYSTEMD_INTERFACE, "StartUnit");
2991eef62deSSaqib Khan     method.append(serviceFile, "replace");
3001eef62deSSaqib Khan     bus.call_noreply(method);
3013526ef73SLeonel Gonzalez 
3023526ef73SLeonel Gonzalez     // Removing entry in activations map
3033526ef73SLeonel Gonzalez     auto ita = activations.find(entryId);
3043526ef73SLeonel Gonzalez     if (ita == activations.end())
3053526ef73SLeonel Gonzalez     {
3062285fe0fSAdriana Kobylak         log<level::ERR>("Error: Failed to find version in item updater "
3072285fe0fSAdriana Kobylak                         "activations map. Unable to remove.",
3082285fe0fSAdriana Kobylak                         entry("VERSIONID=%s", entryId.c_str()));
3093526ef73SLeonel Gonzalez     }
310ee13e831SSaqib Khan     else
311ee13e831SSaqib Khan     {
3123526ef73SLeonel Gonzalez         this->activations.erase(entryId);
313ee13e831SSaqib Khan     }
31449446ae9SSaqib Khan     ItemUpdater::resetUbootEnvVars();
315ee13e831SSaqib Khan     return;
3163526ef73SLeonel Gonzalez }
3173526ef73SLeonel Gonzalez 
318bc1bf3afSMichael Tritz void ItemUpdater::deleteAll()
319bc1bf3afSMichael Tritz {
32083cd21fbSAdriana Kobylak     std::vector<std::string> deletableVersions;
32183cd21fbSAdriana Kobylak 
322bc1bf3afSMichael Tritz     for (const auto& versionIt : versions)
323bc1bf3afSMichael Tritz     {
324bc1bf3afSMichael Tritz         if (!versionIt.second->isFunctional())
325bc1bf3afSMichael Tritz         {
32683cd21fbSAdriana Kobylak             deletableVersions.push_back(versionIt.first);
327bc1bf3afSMichael Tritz         }
328bc1bf3afSMichael Tritz     }
329bc1bf3afSMichael Tritz 
33083cd21fbSAdriana Kobylak     for (const auto& deletableIt : deletableVersions)
33183cd21fbSAdriana Kobylak     {
33283cd21fbSAdriana Kobylak         ItemUpdater::erase(deletableIt);
33383cd21fbSAdriana Kobylak     }
33483cd21fbSAdriana Kobylak 
335bc1bf3afSMichael Tritz     // Remove any volumes that do not match current versions.
3362285fe0fSAdriana Kobylak     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
3372285fe0fSAdriana Kobylak                                       SYSTEMD_INTERFACE, "StartUnit");
338bc1bf3afSMichael Tritz     method.append("obmc-flash-bmc-cleanup.service", "replace");
339bc1bf3afSMichael Tritz     bus.call_noreply(method);
340bc1bf3afSMichael Tritz }
341bc1bf3afSMichael Tritz 
3422285fe0fSAdriana Kobylak ItemUpdater::ActivationStatus
3432285fe0fSAdriana Kobylak     ItemUpdater::validateSquashFSImage(const std::string& filePath)
34435e83f3eSSaqib Khan {
345b1cfdf99SMichael Tritz     bool invalid = false;
34635e83f3eSSaqib Khan 
347b1cfdf99SMichael Tritz     for (auto& bmcImage : bmcImages)
348b1cfdf99SMichael Tritz     {
34919177d3eSSaqib Khan         fs::path file(filePath);
35035e83f3eSSaqib Khan         file /= bmcImage;
35135e83f3eSSaqib Khan         std::ifstream efile(file.c_str());
352b1cfdf99SMichael Tritz         if (efile.good() != 1)
35335e83f3eSSaqib Khan         {
354b1cfdf99SMichael Tritz             log<level::ERR>("Failed to find the BMC image.",
355b1cfdf99SMichael Tritz                             entry("IMAGE=%s", bmcImage.c_str()));
356b1cfdf99SMichael Tritz             invalid = true;
35735e83f3eSSaqib Khan         }
358b1cfdf99SMichael Tritz     }
359b1cfdf99SMichael Tritz 
360b1cfdf99SMichael Tritz     if (invalid)
36135e83f3eSSaqib Khan     {
36235e83f3eSSaqib Khan         return ItemUpdater::ActivationStatus::invalid;
36335e83f3eSSaqib Khan     }
364b1cfdf99SMichael Tritz 
365b1cfdf99SMichael Tritz     return ItemUpdater::ActivationStatus::ready;
36635e83f3eSSaqib Khan }
36735e83f3eSSaqib Khan 
368b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
3694c1aec09SSaqib Khan {
370b77551cdSAdriana Kobylak     std::map<std::string, uint8_t> priorityMap;
371b77551cdSAdriana Kobylak 
372b77551cdSAdriana Kobylak     // Insert the requested version and priority, it may not exist yet.
373b77551cdSAdriana Kobylak     priorityMap.insert(std::make_pair(versionId, value));
374b77551cdSAdriana Kobylak 
3754c1aec09SSaqib Khan     for (const auto& intf : activations)
3764c1aec09SSaqib Khan     {
3774c1aec09SSaqib Khan         if (intf.second->redundancyPriority)
3784c1aec09SSaqib Khan         {
379b77551cdSAdriana Kobylak             priorityMap.insert(std::make_pair(
3802285fe0fSAdriana Kobylak                 intf.first, intf.second->redundancyPriority.get()->priority()));
381b77551cdSAdriana Kobylak         }
382b77551cdSAdriana Kobylak     }
383b77551cdSAdriana Kobylak 
384b77551cdSAdriana Kobylak     // Lambda function to compare 2 priority values, use <= to allow duplicates
3852285fe0fSAdriana Kobylak     typedef std::function<bool(std::pair<std::string, uint8_t>,
3862285fe0fSAdriana Kobylak                                std::pair<std::string, uint8_t>)>
3872285fe0fSAdriana Kobylak         cmpPriority;
3882285fe0fSAdriana Kobylak     cmpPriority cmpPriorityFunc =
3892285fe0fSAdriana Kobylak         [](std::pair<std::string, uint8_t> priority1,
3902285fe0fSAdriana Kobylak            std::pair<std::string, uint8_t> priority2) {
391b77551cdSAdriana Kobylak             return priority1.second <= priority2.second;
392b77551cdSAdriana Kobylak         };
393b77551cdSAdriana Kobylak 
394b77551cdSAdriana Kobylak     // Sort versions by ascending priority
395b77551cdSAdriana Kobylak     std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet(
396b77551cdSAdriana Kobylak         priorityMap.begin(), priorityMap.end(), cmpPriorityFunc);
397b77551cdSAdriana Kobylak 
398b77551cdSAdriana Kobylak     auto freePriorityValue = value;
399b77551cdSAdriana Kobylak     for (auto& element : prioritySet)
400b77551cdSAdriana Kobylak     {
401b77551cdSAdriana Kobylak         if (element.first == versionId)
402b77551cdSAdriana Kobylak         {
403b77551cdSAdriana Kobylak             continue;
404b77551cdSAdriana Kobylak         }
405b77551cdSAdriana Kobylak         if (element.second == freePriorityValue)
406b77551cdSAdriana Kobylak         {
407b77551cdSAdriana Kobylak             ++freePriorityValue;
408b77551cdSAdriana Kobylak             auto it = activations.find(element.first);
409b77551cdSAdriana Kobylak             it->second->redundancyPriority.get()->sdbusPriority(
410b77551cdSAdriana Kobylak                 freePriorityValue);
4114c1aec09SSaqib Khan         }
4124c1aec09SSaqib Khan     }
413b77551cdSAdriana Kobylak 
414b77551cdSAdriana Kobylak     auto lowestVersion = prioritySet.begin()->first;
415b77551cdSAdriana Kobylak     if (value == prioritySet.begin()->second)
416b77551cdSAdriana Kobylak     {
417b77551cdSAdriana Kobylak         lowestVersion = versionId;
4184c1aec09SSaqib Khan     }
419b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestVersion);
4204c1aec09SSaqib Khan }
4214c1aec09SSaqib Khan 
42237a59043SMichael Tritz void ItemUpdater::reset()
42337a59043SMichael Tritz {
42437a59043SMichael Tritz     // Mark the read-write partition for recreation upon reboot.
4252285fe0fSAdriana Kobylak     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
4262285fe0fSAdriana Kobylak                                       SYSTEMD_INTERFACE, "StartUnit");
4270129d926SMichael Tritz     method.append("obmc-flash-bmc-setenv@rwreset\\x3dtrue.service", "replace");
42837a59043SMichael Tritz     bus.call_noreply(method);
42937a59043SMichael Tritz 
43037a59043SMichael Tritz     log<level::INFO>("BMC factory reset will take effect upon reboot.");
43137a59043SMichael Tritz 
43237a59043SMichael Tritz     return;
43337a59043SMichael Tritz }
43437a59043SMichael Tritz 
4353526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId)
4363526ef73SLeonel Gonzalez {
4372285fe0fSAdriana Kobylak     auto serviceFile = "obmc-flash-bmc-ubiro-remove@" + versionId + ".service";
4383526ef73SLeonel Gonzalez 
4393526ef73SLeonel Gonzalez     // Remove the read-only partitions.
4402285fe0fSAdriana Kobylak     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
4412285fe0fSAdriana Kobylak                                       SYSTEMD_INTERFACE, "StartUnit");
4423526ef73SLeonel Gonzalez     method.append(serviceFile, "replace");
4433526ef73SLeonel Gonzalez     bus.call_noreply(method);
4443526ef73SLeonel Gonzalez }
4453526ef73SLeonel Gonzalez 
4460129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value)
4470129d926SMichael Tritz {
4480129d926SMichael Tritz     // enabling field mode is intended to be one way: false -> true
4490129d926SMichael Tritz     if (value && !control::FieldMode::fieldModeEnabled())
4500129d926SMichael Tritz     {
4510129d926SMichael Tritz         control::FieldMode::fieldModeEnabled(value);
4520129d926SMichael Tritz 
4532285fe0fSAdriana Kobylak         auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
4542285fe0fSAdriana Kobylak                                           SYSTEMD_INTERFACE, "StartUnit");
4550129d926SMichael Tritz         method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service",
4560129d926SMichael Tritz                       "replace");
4570129d926SMichael Tritz         bus.call_noreply(method);
4580129d926SMichael Tritz 
4592285fe0fSAdriana Kobylak         method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
4602285fe0fSAdriana Kobylak                                      SYSTEMD_INTERFACE, "StopUnit");
4610129d926SMichael Tritz         method.append("usr-local.mount", "replace");
4620129d926SMichael Tritz         bus.call_noreply(method);
4630129d926SMichael Tritz 
4640129d926SMichael Tritz         std::vector<std::string> usrLocal = {"usr-local.mount"};
4650129d926SMichael Tritz 
4662285fe0fSAdriana Kobylak         method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
4672285fe0fSAdriana Kobylak                                      SYSTEMD_INTERFACE, "MaskUnitFiles");
4680129d926SMichael Tritz         method.append(usrLocal, false, true);
4690129d926SMichael Tritz         bus.call_noreply(method);
4700129d926SMichael Tritz     }
4710129d926SMichael Tritz 
4720129d926SMichael Tritz     return control::FieldMode::fieldModeEnabled();
4730129d926SMichael Tritz }
4740129d926SMichael Tritz 
4750129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus()
4760129d926SMichael Tritz {
477ff0b421dSMichael Tritz     std::ifstream input("/dev/mtd/u-boot-env");
4780129d926SMichael Tritz     std::string envVar;
4790129d926SMichael Tritz     std::getline(input, envVar);
4800129d926SMichael Tritz 
4810129d926SMichael Tritz     if (envVar.find("fieldmode=true") != std::string::npos)
4820129d926SMichael Tritz     {
4830129d926SMichael Tritz         ItemUpdater::fieldModeEnabled(true);
4840129d926SMichael Tritz     }
4850129d926SMichael Tritz }
4860129d926SMichael Tritz 
487b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath()
488b60add1eSGunnar Mills {
489b60add1eSGunnar Mills     auto depth = 0;
4902285fe0fSAdriana Kobylak     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
4912285fe0fSAdriana Kobylak                                           MAPPER_INTERFACE, "GetSubTreePaths");
492b60add1eSGunnar Mills 
4931254c628SAdriana Kobylak     mapperCall.append(INVENTORY_PATH);
494b60add1eSGunnar Mills     mapperCall.append(depth);
4951254c628SAdriana Kobylak     std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE};
496b60add1eSGunnar Mills     mapperCall.append(filter);
497b60add1eSGunnar Mills 
498b60add1eSGunnar Mills     auto response = bus.call(mapperCall);
499b60add1eSGunnar Mills     if (response.is_method_error())
500b60add1eSGunnar Mills     {
501b60add1eSGunnar Mills         log<level::ERR>("Error in mapper GetSubTreePath");
502b60add1eSGunnar Mills         return;
503b60add1eSGunnar Mills     }
504b60add1eSGunnar Mills 
505b60add1eSGunnar Mills     using ObjectPaths = std::vector<std::string>;
506b60add1eSGunnar Mills     ObjectPaths result;
507b60add1eSGunnar Mills     response.read(result);
508b60add1eSGunnar Mills 
5091254c628SAdriana Kobylak     if (!result.empty())
510b60add1eSGunnar Mills     {
5111254c628SAdriana Kobylak         bmcInventoryPath = result.front();
512b60add1eSGunnar Mills     }
513b60add1eSGunnar Mills 
514b60add1eSGunnar Mills     return;
515b60add1eSGunnar Mills }
516b60add1eSGunnar Mills 
517f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path)
518ded875dcSGunnar Mills {
5192285fe0fSAdriana Kobylak     assocs.emplace_back(
5202285fe0fSAdriana Kobylak         std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
521ded875dcSGunnar Mills     associations(assocs);
522ded875dcSGunnar Mills }
523ded875dcSGunnar Mills 
52488e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path)
52588e8a325SGunnar Mills {
52688e8a325SGunnar Mills     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
5272285fe0fSAdriana Kobylak                                         FUNCTIONAL_REV_ASSOCIATION, path));
52888e8a325SGunnar Mills     associations(assocs);
52988e8a325SGunnar Mills }
53088e8a325SGunnar Mills 
531f10b2326SGunnar Mills void ItemUpdater::removeActiveAssociation(const std::string& path)
532ded875dcSGunnar Mills {
533ded875dcSGunnar Mills     for (auto iter = assocs.begin(); iter != assocs.end();)
534ded875dcSGunnar Mills     {
53588e8a325SGunnar Mills         // Since there could be multiple associations to the same path,
53688e8a325SGunnar Mills         // only remove ones that have an active forward association.
53788e8a325SGunnar Mills         if ((std::get<0>(*iter)).compare(ACTIVE_FWD_ASSOCIATION) == 0 &&
53888e8a325SGunnar Mills             (std::get<2>(*iter)).compare(path) == 0)
539ded875dcSGunnar Mills         {
540ded875dcSGunnar Mills             iter = assocs.erase(iter);
541ded875dcSGunnar Mills             associations(assocs);
542ded875dcSGunnar Mills         }
543ded875dcSGunnar Mills         else
544ded875dcSGunnar Mills         {
545ded875dcSGunnar Mills             ++iter;
546ded875dcSGunnar Mills         }
547ded875dcSGunnar Mills     }
548ded875dcSGunnar Mills }
549ded875dcSGunnar Mills 
550b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value)
551b9da6634SSaqib Khan {
552b9da6634SSaqib Khan     for (const auto& intf : activations)
553b9da6634SSaqib Khan     {
554b9da6634SSaqib Khan         if (intf.second->redundancyPriority)
555b9da6634SSaqib Khan         {
556b9da6634SSaqib Khan             if (intf.second->redundancyPriority.get()->priority() < value)
557b9da6634SSaqib Khan             {
558b9da6634SSaqib Khan                 return false;
559b9da6634SSaqib Khan             }
560b9da6634SSaqib Khan         }
561b9da6634SSaqib Khan     }
562b9da6634SSaqib Khan     return true;
563b9da6634SSaqib Khan }
564b9da6634SSaqib Khan 
565b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId)
566b77551cdSAdriana Kobylak {
5672285fe0fSAdriana Kobylak     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
5682285fe0fSAdriana Kobylak                                       SYSTEMD_INTERFACE, "StartUnit");
5692285fe0fSAdriana Kobylak     auto updateEnvVarsFile =
5702285fe0fSAdriana Kobylak         "obmc-flash-bmc-updateubootvars@" + versionId + ".service";
571b77551cdSAdriana Kobylak     method.append(updateEnvVarsFile, "replace");
572b77551cdSAdriana Kobylak     auto result = bus.call(method);
573b77551cdSAdriana Kobylak 
574b77551cdSAdriana Kobylak     // Check that the bus call didn't result in an error
575b77551cdSAdriana Kobylak     if (result.is_method_error())
576b77551cdSAdriana Kobylak     {
577b77551cdSAdriana Kobylak         log<level::ERR>("Failed to update u-boot env variables",
578596466b8SAdriana Kobylak                         entry("VERSIONID=%s", versionId.c_str()));
579b77551cdSAdriana Kobylak     }
580b77551cdSAdriana Kobylak }
581b77551cdSAdriana Kobylak 
58249446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars()
58349446ae9SSaqib Khan {
58449446ae9SSaqib Khan     decltype(activations.begin()->second->redundancyPriority.get()->priority())
58549446ae9SSaqib Khan         lowestPriority = std::numeric_limits<uint8_t>::max();
58649446ae9SSaqib Khan     decltype(activations.begin()->second->versionId) lowestPriorityVersion;
58749446ae9SSaqib Khan     for (const auto& intf : activations)
58849446ae9SSaqib Khan     {
58949446ae9SSaqib Khan         if (!intf.second->redundancyPriority.get())
59049446ae9SSaqib Khan         {
59149446ae9SSaqib Khan             // Skip this version if the redundancyPriority is not initialized.
59249446ae9SSaqib Khan             continue;
59349446ae9SSaqib Khan         }
59449446ae9SSaqib Khan 
5952285fe0fSAdriana Kobylak         if (intf.second->redundancyPriority.get()->priority() <= lowestPriority)
59649446ae9SSaqib Khan         {
59749446ae9SSaqib Khan             lowestPriority = intf.second->redundancyPriority.get()->priority();
59849446ae9SSaqib Khan             lowestPriorityVersion = intf.second->versionId;
59949446ae9SSaqib Khan         }
60049446ae9SSaqib Khan     }
60149446ae9SSaqib Khan 
602f0382c35SSaqib Khan     // Update the U-boot environment variable to point to the lowest priority
603b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestPriorityVersion);
60449446ae9SSaqib Khan }
60549446ae9SSaqib Khan 
606204e1e74SAdriana Kobylak void ItemUpdater::freeSpace()
607204e1e74SAdriana Kobylak {
608204e1e74SAdriana Kobylak     //  Versions with the highest priority in front
609204e1e74SAdriana Kobylak     std::priority_queue<std::pair<int, std::string>,
610204e1e74SAdriana Kobylak                         std::vector<std::pair<int, std::string>>,
6112285fe0fSAdriana Kobylak                         std::less<std::pair<int, std::string>>>
6122285fe0fSAdriana Kobylak         versionsPQ;
613204e1e74SAdriana Kobylak 
614204e1e74SAdriana Kobylak     std::size_t count = 0;
615204e1e74SAdriana Kobylak     for (const auto& iter : activations)
616204e1e74SAdriana Kobylak     {
617204e1e74SAdriana Kobylak         if ((iter.second.get()->activation() ==
618204e1e74SAdriana Kobylak              server::Activation::Activations::Active) ||
619204e1e74SAdriana Kobylak             (iter.second.get()->activation() ==
620204e1e74SAdriana Kobylak              server::Activation::Activations::Failed))
621204e1e74SAdriana Kobylak         {
622204e1e74SAdriana Kobylak             count++;
623204e1e74SAdriana Kobylak             // Don't put the functional version on the queue since we can't
624204e1e74SAdriana Kobylak             // remove the "running" BMC version.
625204e1e74SAdriana Kobylak             if (versions.find(iter.second->versionId)->second->isFunctional())
626204e1e74SAdriana Kobylak             {
627204e1e74SAdriana Kobylak                 continue;
628204e1e74SAdriana Kobylak             }
629204e1e74SAdriana Kobylak             versionsPQ.push(std::make_pair(
630204e1e74SAdriana Kobylak                 iter.second->redundancyPriority.get()->priority(),
631204e1e74SAdriana Kobylak                 iter.second->versionId));
632204e1e74SAdriana Kobylak         }
633204e1e74SAdriana Kobylak     }
634204e1e74SAdriana Kobylak 
635204e1e74SAdriana Kobylak     // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1,
636204e1e74SAdriana Kobylak     // remove the highest priority one(s).
637204e1e74SAdriana Kobylak     while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty()))
638204e1e74SAdriana Kobylak     {
639204e1e74SAdriana Kobylak         erase(versionsPQ.top().second);
640204e1e74SAdriana Kobylak         versionsPQ.pop();
641204e1e74SAdriana Kobylak         count--;
642204e1e74SAdriana Kobylak     }
643204e1e74SAdriana Kobylak }
644204e1e74SAdriana Kobylak 
645eaa1ee05SEddie James void ItemUpdater::mirrorUbootToAlt()
646eaa1ee05SEddie James {
647eaa1ee05SEddie James     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
648eaa1ee05SEddie James                                       SYSTEMD_INTERFACE, "StartUnit");
649eaa1ee05SEddie James     auto mirrorUbootFile = "obmc-flash-bmc-mirroruboot.service";
650eaa1ee05SEddie James     method.append(mirrorUbootFile, "replace");
651eaa1ee05SEddie James     auto result = bus.call(method);
652eaa1ee05SEddie James 
653eaa1ee05SEddie James     // Check that the bus call didn't result in an error
654eaa1ee05SEddie James     if (result.is_method_error())
655eaa1ee05SEddie James     {
656eaa1ee05SEddie James         log<level::ERR>("Failed to copy U-Boot to alternate chip");
657eaa1ee05SEddie James     }
658eaa1ee05SEddie James }
659eaa1ee05SEddie James 
660ec1b41c4SGunnar Mills } // namespace updater
661ec1b41c4SGunnar Mills } // namespace software
662ec1b41c4SGunnar Mills } // namespace phosphor
663