135e83f3eSSaqib Khan #include <fstream>
2ec1b41c4SGunnar Mills #include <string>
32ce7da29SGunnar Mills #include <phosphor-logging/log.hpp>
4dcbfa04aSSaqib Khan #include <phosphor-logging/elog.hpp>
5dcbfa04aSSaqib Khan #include <elog-errors.hpp>
6dcbfa04aSSaqib Khan #include <xyz/openbmc_project/Software/Version/error.hpp>
7ec1b41c4SGunnar Mills #include "config.h"
82ce7da29SGunnar Mills #include "item_updater.hpp"
92ce7da29SGunnar Mills #include "xyz/openbmc_project/Software/Version/server.hpp"
1035e83f3eSSaqib Khan #include <experimental/filesystem>
11705f1bfcSSaqib Khan #include "version.hpp"
125d532675SSaqib Khan #include "serialize.hpp"
13ec1b41c4SGunnar Mills 
14ec1b41c4SGunnar Mills namespace phosphor
15ec1b41c4SGunnar Mills {
16ec1b41c4SGunnar Mills namespace software
17ec1b41c4SGunnar Mills {
18ec1b41c4SGunnar Mills namespace updater
19ec1b41c4SGunnar Mills {
20ec1b41c4SGunnar Mills 
212ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class
222ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server;
230129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server;
242ce7da29SGunnar Mills 
252ce7da29SGunnar Mills using namespace phosphor::logging;
26dcbfa04aSSaqib Khan using namespace sdbusplus::xyz::openbmc_project::Software::Version::Error;
2735e83f3eSSaqib Khan namespace fs = std::experimental::filesystem;
2835e83f3eSSaqib Khan 
29b1cfdf99SMichael Tritz const std::vector<std::string> bmcImages = { "image-kernel",
30b1cfdf99SMichael Tritz                                              "image-rofs",
31b1cfdf99SMichael Tritz                                              "image-rwfs",
32b1cfdf99SMichael Tritz                                              "image-u-boot" };
332ce7da29SGunnar Mills 
34e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg)
35ec1b41c4SGunnar Mills {
3684a0e693SSaqib Khan 
3784a0e693SSaqib Khan     using SVersion = server::Version;
3884a0e693SSaqib Khan     using VersionPurpose = SVersion::VersionPurpose;
399a782243SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
4084a0e693SSaqib Khan     namespace mesg = sdbusplus::message;
4184a0e693SSaqib Khan     namespace variant_ns = mesg::variant_ns;
4284a0e693SSaqib Khan 
4384a0e693SSaqib Khan     mesg::object_path objPath;
4484a0e693SSaqib Khan     auto purpose = VersionPurpose::Unknown;
45705f1bfcSSaqib Khan     std::string version;
462ce7da29SGunnar Mills     std::map<std::string,
472ce7da29SGunnar Mills              std::map<std::string,
4884a0e693SSaqib Khan                       mesg::variant<std::string>>> interfaces;
49e75d10f5SPatrick Williams     msg.read(objPath, interfaces);
502ce7da29SGunnar Mills     std::string path(std::move(objPath));
5119177d3eSSaqib Khan     std::string filePath;
522ce7da29SGunnar Mills 
532ce7da29SGunnar Mills     for (const auto& intf : interfaces)
542ce7da29SGunnar Mills     {
55705f1bfcSSaqib Khan         if (intf.first == VERSION_IFACE)
562ce7da29SGunnar Mills         {
572ce7da29SGunnar Mills             for (const auto& property : intf.second)
582ce7da29SGunnar Mills             {
59705f1bfcSSaqib Khan                 if (property.first == "Purpose")
602ce7da29SGunnar Mills                 {
6184a0e693SSaqib Khan                     auto value = SVersion::convertVersionPurposeFromString(
6284a0e693SSaqib Khan                             variant_ns::get<std::string>(property.second));
6384a0e693SSaqib Khan                     if (value == VersionPurpose::BMC ||
6484a0e693SSaqib Khan                         value == VersionPurpose::System)
6584a0e693SSaqib Khan                     {
6684a0e693SSaqib Khan                         purpose = value;
6784a0e693SSaqib Khan                     }
68705f1bfcSSaqib Khan                 }
69705f1bfcSSaqib Khan                 else if (property.first == "Version")
70705f1bfcSSaqib Khan                 {
7184a0e693SSaqib Khan                     version = variant_ns::get<std::string>(property.second);
72705f1bfcSSaqib Khan                 }
73705f1bfcSSaqib Khan             }
74705f1bfcSSaqib Khan         }
7519177d3eSSaqib Khan         else if (intf.first == FILEPATH_IFACE)
7619177d3eSSaqib Khan         {
7719177d3eSSaqib Khan             for (const auto& property : intf.second)
7819177d3eSSaqib Khan             {
7919177d3eSSaqib Khan                 if (property.first == "Path")
8019177d3eSSaqib Khan                 {
8184a0e693SSaqib Khan                     filePath = variant_ns::get<std::string>(property.second);
8219177d3eSSaqib Khan                 }
8319177d3eSSaqib Khan             }
8419177d3eSSaqib Khan         }
85705f1bfcSSaqib Khan     }
86705f1bfcSSaqib Khan     if (version.empty() ||
8719177d3eSSaqib Khan         filePath.empty() ||
8884a0e693SSaqib Khan         purpose == VersionPurpose::Unknown)
892ce7da29SGunnar Mills     {
90e75d10f5SPatrick Williams         return;
912ce7da29SGunnar Mills     }
922ce7da29SGunnar Mills 
932ce7da29SGunnar Mills     // Version id is the last item in the path
942ce7da29SGunnar Mills     auto pos = path.rfind("/");
952ce7da29SGunnar Mills     if (pos == std::string::npos)
962ce7da29SGunnar Mills     {
972ce7da29SGunnar Mills         log<level::ERR>("No version id found in object path",
982ce7da29SGunnar Mills                         entry("OBJPATH=%s", path));
99e75d10f5SPatrick Williams         return;
1002ce7da29SGunnar Mills     }
1012ce7da29SGunnar Mills 
1022ce7da29SGunnar Mills     auto versionId = path.substr(pos + 1);
1032ce7da29SGunnar Mills 
104e75d10f5SPatrick Williams     if (activations.find(versionId) == activations.end())
1052ce7da29SGunnar Mills     {
10635e83f3eSSaqib Khan         // Determine the Activation state by processing the given image dir.
10735e83f3eSSaqib Khan         auto activationState = server::Activation::Activations::Invalid;
1089a782243SGunnar Mills         ItemUpdater::ActivationStatus result =
1099a782243SGunnar Mills                 ItemUpdater::validateSquashFSImage(filePath);
11043b25cdeSGunnar Mills         AssociationList associations = {};
11143b25cdeSGunnar Mills 
11235e83f3eSSaqib Khan         if (result == ItemUpdater::ActivationStatus::ready)
11335e83f3eSSaqib Khan         {
11435e83f3eSSaqib Khan             activationState = server::Activation::Activations::Ready;
115b60add1eSGunnar Mills             // Create an association to the BMC inventory item
11643b25cdeSGunnar Mills             associations.emplace_back(std::make_tuple(
117b60add1eSGunnar Mills                                               ACTIVATION_FWD_ASSOCIATION,
118b60add1eSGunnar Mills                                               ACTIVATION_REV_ASSOCIATION,
11943b25cdeSGunnar Mills                                               bmcInventoryPath));
12043b25cdeSGunnar Mills         }
121b60add1eSGunnar Mills 
12235e83f3eSSaqib Khan         activations.insert(std::make_pair(
1232ce7da29SGunnar Mills                                versionId,
124ec1b41c4SGunnar Mills                                std::make_unique<Activation>(
12535e83f3eSSaqib Khan                                         bus,
12635e83f3eSSaqib Khan                                         path,
1274c1aec09SSaqib Khan                                         *this,
12835e83f3eSSaqib Khan                                         versionId,
129b60add1eSGunnar Mills                                         activationState,
130b60add1eSGunnar Mills                                         associations)));
131705f1bfcSSaqib Khan         versions.insert(std::make_pair(
132705f1bfcSSaqib Khan                             versionId,
1339a782243SGunnar Mills                             std::make_unique<VersionClass>(
134705f1bfcSSaqib Khan                                 bus,
135705f1bfcSSaqib Khan                                 path,
136705f1bfcSSaqib Khan                                 version,
137705f1bfcSSaqib Khan                                 purpose,
1389440f491SEddie James                                 filePath)));
1392ce7da29SGunnar Mills     }
140e75d10f5SPatrick Williams     return;
141ec1b41c4SGunnar Mills }
142ec1b41c4SGunnar Mills 
143ba239881SSaqib Khan void ItemUpdater::processBMCImage()
144ba239881SSaqib Khan {
14588e8a325SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
14688e8a325SGunnar Mills     // Read os-release from /etc/ to get the functional BMC version
14788e8a325SGunnar Mills     auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE);
14888e8a325SGunnar Mills 
1491eef62deSSaqib Khan     // Read os-release from folders under /media/ to get
1501eef62deSSaqib Khan     // BMC Software Versions.
1511eef62deSSaqib Khan     for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
1521eef62deSSaqib Khan     {
1531eef62deSSaqib Khan         auto activationState = server::Activation::Activations::Active;
1546fab70daSSaqib Khan         static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX);
1551eef62deSSaqib Khan 
1561eef62deSSaqib Khan         // Check if the BMC_RO_PREFIXis the prefix of the iter.path
1571eef62deSSaqib Khan         if (0 == iter.path().native().compare(0, BMC_RO_PREFIX_LEN,
1586fab70daSSaqib Khan                                               BMC_ROFS_PREFIX))
1591eef62deSSaqib Khan         {
160021c365bSSaqib Khan             // The versionId is extracted from the path
161021c365bSSaqib Khan             // for example /media/ro-2a1022fe.
162021c365bSSaqib Khan             auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
1631eef62deSSaqib Khan             auto osRelease = iter.path() / OS_RELEASE_FILE;
1641eef62deSSaqib Khan             if (!fs::is_regular_file(osRelease))
1651eef62deSSaqib Khan             {
1662ad1b55fSGunnar Mills                 log<level::ERR>("Failed to read osRelease",
1672ad1b55fSGunnar Mills                                 entry("FILENAME=%s", osRelease.string()));
168021c365bSSaqib Khan                 ItemUpdater::erase(id);
169021c365bSSaqib Khan                 continue;
1701eef62deSSaqib Khan             }
17188e8a325SGunnar Mills             auto version = VersionClass::getBMCVersion(osRelease);
1721eef62deSSaqib Khan             if (version.empty())
1731eef62deSSaqib Khan             {
1741eef62deSSaqib Khan                 log<level::ERR>("Failed to read version from osRelease",
1751eef62deSSaqib Khan                                 entry("FILENAME=%s", osRelease.string()));
1761eef62deSSaqib Khan                 activationState = server::Activation::Activations::Invalid;
1771eef62deSSaqib Khan             }
178021c365bSSaqib Khan 
1791eef62deSSaqib Khan             auto purpose = server::Version::VersionPurpose::BMC;
1801eef62deSSaqib Khan             auto path = fs::path(SOFTWARE_OBJPATH) / id;
1811eef62deSSaqib Khan 
18288e8a325SGunnar Mills             // Create functional association if this is the functional version
18388e8a325SGunnar Mills             if (version.compare(functionalVersion) == 0)
18488e8a325SGunnar Mills             {
18588e8a325SGunnar Mills                 createFunctionalAssociation(path);
18688e8a325SGunnar Mills             }
18788e8a325SGunnar Mills 
18843b25cdeSGunnar Mills             AssociationList associations = {};
18943b25cdeSGunnar Mills 
19043b25cdeSGunnar Mills             if (activationState == server::Activation::Activations::Active)
19143b25cdeSGunnar Mills             {
19243b25cdeSGunnar Mills                 // Create an association to the BMC inventory item
19343b25cdeSGunnar Mills                 associations.emplace_back(std::make_tuple(
19443b25cdeSGunnar Mills                                                   ACTIVATION_FWD_ASSOCIATION,
19543b25cdeSGunnar Mills                                                   ACTIVATION_REV_ASSOCIATION,
19643b25cdeSGunnar Mills                                                   bmcInventoryPath));
19743b25cdeSGunnar Mills 
19843b25cdeSGunnar Mills                 // Create an active association since this image is active
19943b25cdeSGunnar Mills                 createActiveAssociation(path);
20043b25cdeSGunnar Mills             }
20143b25cdeSGunnar Mills 
202ee590c74SAdriana Kobylak             // Create Version instance for this version.
203ee590c74SAdriana Kobylak             auto versionPtr = std::make_unique<VersionClass>(
204ee590c74SAdriana Kobylak                             bus,
205ee590c74SAdriana Kobylak                             path,
206ee590c74SAdriana Kobylak                             version,
207ee590c74SAdriana Kobylak                             purpose,
208ee590c74SAdriana Kobylak                             "");
209ee590c74SAdriana Kobylak             auto isVersionFunctional = versionPtr->isFunctional();
210ee590c74SAdriana Kobylak             versions.insert(std::make_pair(
211ee590c74SAdriana Kobylak                                 id,
212ee590c74SAdriana Kobylak                                 std::move(versionPtr)));
213ee590c74SAdriana Kobylak 
2141eef62deSSaqib Khan             // Create Activation instance for this version.
215ba239881SSaqib Khan             activations.insert(std::make_pair(
216ba239881SSaqib Khan                                    id,
217ba239881SSaqib Khan                                    std::make_unique<Activation>(
218ba239881SSaqib Khan                                        bus,
219ba239881SSaqib Khan                                        path,
2204c1aec09SSaqib Khan                                        *this,
221ba239881SSaqib Khan                                        id,
222b60add1eSGunnar Mills                                        server::Activation::Activations::Active,
223b60add1eSGunnar Mills                                        associations)));
2241eef62deSSaqib Khan 
2251eef62deSSaqib Khan             // If Active, create RedundancyPriority instance for this version.
2261eef62deSSaqib Khan             if (activationState == server::Activation::Activations::Active)
2271eef62deSSaqib Khan             {
2281eef62deSSaqib Khan                 uint8_t priority = std::numeric_limits<uint8_t>::max();
2291eef62deSSaqib Khan                 if (!restoreFromFile(id, priority))
2301eef62deSSaqib Khan                 {
231ee590c74SAdriana Kobylak                     if (isVersionFunctional)
232ee590c74SAdriana Kobylak                     {
233ee590c74SAdriana Kobylak                         priority = 0;
234ee590c74SAdriana Kobylak                     }
235ee590c74SAdriana Kobylak                     else
236ee590c74SAdriana Kobylak                     {
2371eef62deSSaqib Khan                         log<level::ERR>("Unable to restore priority from file.",
2381eef62deSSaqib Khan                                 entry("VERSIONID=%s", id));
2391eef62deSSaqib Khan                     }
240ee590c74SAdriana Kobylak                 }
2411eef62deSSaqib Khan                 activations.find(id)->second->redundancyPriority =
2421eef62deSSaqib Khan                         std::make_unique<RedundancyPriority>(
2431eef62deSSaqib Khan                              bus,
2441eef62deSSaqib Khan                              path,
2451eef62deSSaqib Khan                              *(activations.find(id)->second),
2461eef62deSSaqib Khan                              priority);
2471eef62deSSaqib Khan             }
2481eef62deSSaqib Khan         }
2491eef62deSSaqib Khan     }
250dcbfa04aSSaqib Khan 
251dcbfa04aSSaqib Khan     // If there is no ubi volume for bmc version then read the /etc/os-release
252dcbfa04aSSaqib Khan     // and create rofs-<versionId> under /media
253dcbfa04aSSaqib Khan     if (activations.size() == 0)
254dcbfa04aSSaqib Khan     {
255d16bcbd5SGunnar Mills         auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE);
256dcbfa04aSSaqib Khan         auto id = phosphor::software::manager::Version::getId(version);
257dcbfa04aSSaqib Khan         auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/";
258dcbfa04aSSaqib Khan         try
259dcbfa04aSSaqib Khan         {
260dcbfa04aSSaqib Khan             if (!fs::is_directory(versionFileDir))
261dcbfa04aSSaqib Khan             {
262dcbfa04aSSaqib Khan                 fs::create_directories(versionFileDir);
263dcbfa04aSSaqib Khan             }
264dcbfa04aSSaqib Khan             auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE;
265dcbfa04aSSaqib Khan             fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath);
266dcbfa04aSSaqib Khan             ItemUpdater::processBMCImage();
267dcbfa04aSSaqib Khan         }
268dcbfa04aSSaqib Khan         catch (const std::exception& e)
269dcbfa04aSSaqib Khan         {
270dcbfa04aSSaqib Khan             log<level::ERR>(e.what());
271dcbfa04aSSaqib Khan         }
272dcbfa04aSSaqib Khan     }
273ba239881SSaqib Khan     return;
274ba239881SSaqib Khan }
275ba239881SSaqib Khan 
2763526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId)
2773526ef73SLeonel Gonzalez {
2786d873715SEddie James     // Find entry in versions map
2796d873715SEddie James     auto it = versions.find(entryId);
2806d873715SEddie James     if (it != versions.end())
2816d873715SEddie James     {
2826d873715SEddie James         if (it->second->isFunctional())
2836d873715SEddie James         {
2846d873715SEddie James             log<level::ERR>(("Error: Version " + entryId + \
2856d873715SEddie James                              " is currently running on the BMC." \
2866d873715SEddie James                              " Unable to remove.").c_str());
2876d873715SEddie James             return;
2886d873715SEddie James         }
2896d873715SEddie James 
2906d873715SEddie James         // Delete ReadOnly partitions if it's not active
2913526ef73SLeonel Gonzalez         removeReadOnlyPartition(entryId);
2921eef62deSSaqib Khan         removeFile(entryId);
2936d873715SEddie James     }
2946d873715SEddie James     else
2956d873715SEddie James     {
2966d873715SEddie James         // Delete ReadOnly partitions even if we can't find the version
2976d873715SEddie James         removeReadOnlyPartition(entryId);
2986d873715SEddie James         removeFile(entryId);
2996d873715SEddie James 
3006d873715SEddie James         log<level::ERR>(("Error: Failed to find version " + entryId + \
3016d873715SEddie James                          " in item updater versions map." \
3026d873715SEddie James                          " Unable to remove.").c_str());
3036d873715SEddie James         return;
3046d873715SEddie James     }
3051eef62deSSaqib Khan 
3061eef62deSSaqib Khan     // Remove the priority environment variable.
3071eef62deSSaqib Khan     auto serviceFile = "obmc-flash-bmc-setenv@" + entryId + ".service";
3081eef62deSSaqib Khan     auto method = bus.new_method_call(
3091eef62deSSaqib Khan             SYSTEMD_BUSNAME,
3101eef62deSSaqib Khan             SYSTEMD_PATH,
3111eef62deSSaqib Khan             SYSTEMD_INTERFACE,
3121eef62deSSaqib Khan             "StartUnit");
3131eef62deSSaqib Khan     method.append(serviceFile, "replace");
3141eef62deSSaqib Khan     bus.call_noreply(method);
3153526ef73SLeonel Gonzalez 
3163526ef73SLeonel Gonzalez     // Removing entry in versions map
3173526ef73SLeonel Gonzalez     this->versions.erase(entryId);
3183526ef73SLeonel Gonzalez 
3193526ef73SLeonel Gonzalez     // Removing entry in activations map
3203526ef73SLeonel Gonzalez     auto ita = activations.find(entryId);
3213526ef73SLeonel Gonzalez     if (ita == activations.end())
3223526ef73SLeonel Gonzalez     {
3233526ef73SLeonel Gonzalez         log<level::ERR>(("Error: Failed to find version " + entryId + \
3243526ef73SLeonel Gonzalez                          " in item updater activations map." \
3253526ef73SLeonel Gonzalez                          " Unable to remove.").c_str());
3263526ef73SLeonel Gonzalez         return;
3273526ef73SLeonel Gonzalez     }
3283526ef73SLeonel Gonzalez 
3293526ef73SLeonel Gonzalez     this->activations.erase(entryId);
33049446ae9SSaqib Khan     ItemUpdater::resetUbootEnvVars();
3313526ef73SLeonel Gonzalez }
3323526ef73SLeonel Gonzalez 
333bc1bf3afSMichael Tritz void ItemUpdater::deleteAll()
334bc1bf3afSMichael Tritz {
335bc1bf3afSMichael Tritz     std::vector<std::string> deletableVersions;
336bc1bf3afSMichael Tritz 
337bc1bf3afSMichael Tritz     for (const auto& versionIt : versions)
338bc1bf3afSMichael Tritz     {
339bc1bf3afSMichael Tritz         if (!versionIt.second->isFunctional())
340bc1bf3afSMichael Tritz         {
341bc1bf3afSMichael Tritz             deletableVersions.push_back(versionIt.first);
342bc1bf3afSMichael Tritz         }
343bc1bf3afSMichael Tritz     }
344bc1bf3afSMichael Tritz 
345bc1bf3afSMichael Tritz     for (const auto& deletableIt : deletableVersions)
346bc1bf3afSMichael Tritz     {
347bc1bf3afSMichael Tritz         ItemUpdater::erase(deletableIt);
348bc1bf3afSMichael Tritz     }
349bc1bf3afSMichael Tritz 
350bc1bf3afSMichael Tritz     // Remove any volumes that do not match current versions.
351bc1bf3afSMichael Tritz     auto method = bus.new_method_call(
352bc1bf3afSMichael Tritz             SYSTEMD_BUSNAME,
353bc1bf3afSMichael Tritz             SYSTEMD_PATH,
354bc1bf3afSMichael Tritz             SYSTEMD_INTERFACE,
355bc1bf3afSMichael Tritz             "StartUnit");
356bc1bf3afSMichael Tritz     method.append("obmc-flash-bmc-cleanup.service", "replace");
357bc1bf3afSMichael Tritz     bus.call_noreply(method);
358bc1bf3afSMichael Tritz }
359bc1bf3afSMichael Tritz 
36035e83f3eSSaqib Khan ItemUpdater::ActivationStatus ItemUpdater::validateSquashFSImage(
36119177d3eSSaqib Khan         const std::string& filePath)
36235e83f3eSSaqib Khan {
363b1cfdf99SMichael Tritz     bool invalid = false;
36435e83f3eSSaqib Khan 
365b1cfdf99SMichael Tritz     for (auto& bmcImage : bmcImages)
366b1cfdf99SMichael Tritz     {
36719177d3eSSaqib Khan         fs::path file(filePath);
36835e83f3eSSaqib Khan         file /= bmcImage;
36935e83f3eSSaqib Khan         std::ifstream efile(file.c_str());
370b1cfdf99SMichael Tritz         if (efile.good() != 1)
37135e83f3eSSaqib Khan         {
372b1cfdf99SMichael Tritz             log<level::ERR>("Failed to find the BMC image.",
373b1cfdf99SMichael Tritz                             entry("IMAGE=%s", bmcImage.c_str()));
374b1cfdf99SMichael Tritz             invalid = true;
37535e83f3eSSaqib Khan         }
376b1cfdf99SMichael Tritz     }
377b1cfdf99SMichael Tritz 
378b1cfdf99SMichael Tritz     if (invalid)
37935e83f3eSSaqib Khan     {
38035e83f3eSSaqib Khan         return ItemUpdater::ActivationStatus::invalid;
38135e83f3eSSaqib Khan     }
382b1cfdf99SMichael Tritz 
383b1cfdf99SMichael Tritz     return ItemUpdater::ActivationStatus::ready;
38435e83f3eSSaqib Khan }
38535e83f3eSSaqib Khan 
386b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
3874c1aec09SSaqib Khan {
3884c1aec09SSaqib Khan     //TODO openbmc/openbmc#1896 Improve the performance of this function
3894c1aec09SSaqib Khan     for (const auto& intf : activations)
3904c1aec09SSaqib Khan     {
3914c1aec09SSaqib Khan         if (intf.second->redundancyPriority)
3924c1aec09SSaqib Khan         {
393b9da6634SSaqib Khan             if (intf.second->redundancyPriority.get()->priority() == value &&
394b9da6634SSaqib Khan                 intf.second->versionId != versionId)
3954c1aec09SSaqib Khan             {
3964c1aec09SSaqib Khan                 intf.second->redundancyPriority.get()->priority(value + 1);
3974c1aec09SSaqib Khan             }
3984c1aec09SSaqib Khan         }
3994c1aec09SSaqib Khan     }
4004c1aec09SSaqib Khan }
4014c1aec09SSaqib Khan 
40237a59043SMichael Tritz void ItemUpdater::reset()
40337a59043SMichael Tritz {
40437a59043SMichael Tritz     // Mark the read-write partition for recreation upon reboot.
40537a59043SMichael Tritz     auto method = bus.new_method_call(
40637a59043SMichael Tritz             SYSTEMD_BUSNAME,
40737a59043SMichael Tritz             SYSTEMD_PATH,
40837a59043SMichael Tritz             SYSTEMD_INTERFACE,
40937a59043SMichael Tritz             "StartUnit");
4100129d926SMichael Tritz     method.append("obmc-flash-bmc-setenv@rwreset\\x3dtrue.service", "replace");
41137a59043SMichael Tritz     bus.call_noreply(method);
41237a59043SMichael Tritz 
41337a59043SMichael Tritz     log<level::INFO>("BMC factory reset will take effect upon reboot.");
41437a59043SMichael Tritz 
41537a59043SMichael Tritz     return;
41637a59043SMichael Tritz }
41737a59043SMichael Tritz 
4183526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId)
4193526ef73SLeonel Gonzalez {
4203526ef73SLeonel Gonzalez     auto serviceFile = "obmc-flash-bmc-ubiro-remove@" + versionId +
4213526ef73SLeonel Gonzalez             ".service";
4223526ef73SLeonel Gonzalez 
4233526ef73SLeonel Gonzalez     // Remove the read-only partitions.
4243526ef73SLeonel Gonzalez     auto method = bus.new_method_call(
4253526ef73SLeonel Gonzalez             SYSTEMD_BUSNAME,
4263526ef73SLeonel Gonzalez             SYSTEMD_PATH,
4273526ef73SLeonel Gonzalez             SYSTEMD_INTERFACE,
4283526ef73SLeonel Gonzalez             "StartUnit");
4293526ef73SLeonel Gonzalez     method.append(serviceFile, "replace");
4303526ef73SLeonel Gonzalez     bus.call_noreply(method);
4313526ef73SLeonel Gonzalez }
4323526ef73SLeonel Gonzalez 
4330129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value)
4340129d926SMichael Tritz {
4350129d926SMichael Tritz     // enabling field mode is intended to be one way: false -> true
4360129d926SMichael Tritz     if (value && !control::FieldMode::fieldModeEnabled())
4370129d926SMichael Tritz     {
4380129d926SMichael Tritz         control::FieldMode::fieldModeEnabled(value);
4390129d926SMichael Tritz 
4400129d926SMichael Tritz         auto method = bus.new_method_call(
4410129d926SMichael Tritz                 SYSTEMD_BUSNAME,
4420129d926SMichael Tritz                 SYSTEMD_PATH,
4430129d926SMichael Tritz                 SYSTEMD_INTERFACE,
4440129d926SMichael Tritz                 "StartUnit");
4450129d926SMichael Tritz         method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service",
4460129d926SMichael Tritz                       "replace");
4470129d926SMichael Tritz         bus.call_noreply(method);
4480129d926SMichael Tritz 
4490129d926SMichael Tritz         method = bus.new_method_call(
4500129d926SMichael Tritz                 SYSTEMD_BUSNAME,
4510129d926SMichael Tritz                 SYSTEMD_PATH,
4520129d926SMichael Tritz                 SYSTEMD_INTERFACE,
4530129d926SMichael Tritz                 "StopUnit");
4540129d926SMichael Tritz         method.append("usr-local.mount", "replace");
4550129d926SMichael Tritz         bus.call_noreply(method);
4560129d926SMichael Tritz 
4570129d926SMichael Tritz         std::vector<std::string> usrLocal = {"usr-local.mount"};
4580129d926SMichael Tritz 
4590129d926SMichael Tritz         method = bus.new_method_call(
4600129d926SMichael Tritz                 SYSTEMD_BUSNAME,
4610129d926SMichael Tritz                 SYSTEMD_PATH,
4620129d926SMichael Tritz                 SYSTEMD_INTERFACE,
4630129d926SMichael Tritz                 "MaskUnitFiles");
4640129d926SMichael Tritz         method.append(usrLocal, false, true);
4650129d926SMichael Tritz         bus.call_noreply(method);
4660129d926SMichael Tritz     }
4670129d926SMichael Tritz 
4680129d926SMichael Tritz     return control::FieldMode::fieldModeEnabled();
4690129d926SMichael Tritz }
4700129d926SMichael Tritz 
4710129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus()
4720129d926SMichael Tritz {
473*ff0b421dSMichael Tritz     std::ifstream input("/dev/mtd/u-boot-env");
4740129d926SMichael Tritz     std::string envVar;
4750129d926SMichael Tritz     std::getline(input, envVar);
4760129d926SMichael Tritz 
4770129d926SMichael Tritz     if (envVar.find("fieldmode=true") != std::string::npos)
4780129d926SMichael Tritz     {
4790129d926SMichael Tritz         ItemUpdater::fieldModeEnabled(true);
4800129d926SMichael Tritz     }
4810129d926SMichael Tritz }
4820129d926SMichael Tritz 
483b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath()
484b60add1eSGunnar Mills {
485b60add1eSGunnar Mills     //TODO: openbmc/openbmc#1786 - Get the BMC path by looking for objects
486b60add1eSGunnar Mills     //      that implement the BMC inventory interface
487b60add1eSGunnar Mills     auto depth = 0;
488b60add1eSGunnar Mills     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME,
489b60add1eSGunnar Mills                                           MAPPER_PATH,
490b60add1eSGunnar Mills                                           MAPPER_INTERFACE,
491b60add1eSGunnar Mills                                           "GetSubTreePaths");
492b60add1eSGunnar Mills 
493b60add1eSGunnar Mills     mapperCall.append(CHASSIS_INVENTORY_PATH);
494b60add1eSGunnar Mills     mapperCall.append(depth);
495b60add1eSGunnar Mills 
496b60add1eSGunnar Mills     // TODO: openbmc/openbmc#2226 - Add Inventory Item filter when
497b60add1eSGunnar Mills     //       mapper is fixed.
498b60add1eSGunnar Mills     std::vector<std::string> filter = {};
499b60add1eSGunnar Mills     mapperCall.append(filter);
500b60add1eSGunnar Mills 
501b60add1eSGunnar Mills     auto response = bus.call(mapperCall);
502b60add1eSGunnar Mills     if (response.is_method_error())
503b60add1eSGunnar Mills     {
504b60add1eSGunnar Mills         log<level::ERR>("Error in mapper GetSubTreePath");
505b60add1eSGunnar Mills         return;
506b60add1eSGunnar Mills     }
507b60add1eSGunnar Mills 
508b60add1eSGunnar Mills     using ObjectPaths = std::vector<std::string>;
509b60add1eSGunnar Mills     ObjectPaths result;
510b60add1eSGunnar Mills     response.read(result);
511b60add1eSGunnar Mills 
512b60add1eSGunnar Mills     if (result.empty())
513b60add1eSGunnar Mills     {
514b60add1eSGunnar Mills         log<level::ERR>("Invalid response from mapper");
515b60add1eSGunnar Mills         return;
516b60add1eSGunnar Mills     }
517b60add1eSGunnar Mills 
518b60add1eSGunnar Mills     for (auto& iter : result)
519b60add1eSGunnar Mills     {
520b60add1eSGunnar Mills         const auto& path = iter;
521b60add1eSGunnar Mills         if (path.substr(path.find_last_of('/') + 1).compare("bmc") == 0)
522b60add1eSGunnar Mills         {
523b60add1eSGunnar Mills             bmcInventoryPath = path;
524b60add1eSGunnar Mills             return;
525b60add1eSGunnar Mills         }
526b60add1eSGunnar Mills     }
527b60add1eSGunnar Mills }
528b60add1eSGunnar Mills 
529f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path)
530ded875dcSGunnar Mills {
531ded875dcSGunnar Mills     assocs.emplace_back(std::make_tuple(ACTIVE_FWD_ASSOCIATION,
532ded875dcSGunnar Mills                                         ACTIVE_REV_ASSOCIATION,
533ded875dcSGunnar Mills                                         path));
534ded875dcSGunnar Mills     associations(assocs);
535ded875dcSGunnar Mills }
536ded875dcSGunnar Mills 
53788e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path)
53888e8a325SGunnar Mills {
53988e8a325SGunnar Mills     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
54088e8a325SGunnar Mills                                         FUNCTIONAL_REV_ASSOCIATION,
54188e8a325SGunnar Mills                                         path));
54288e8a325SGunnar Mills     associations(assocs);
54388e8a325SGunnar Mills }
54488e8a325SGunnar Mills 
545f10b2326SGunnar Mills void ItemUpdater::removeActiveAssociation(const std::string& path)
546ded875dcSGunnar Mills {
547ded875dcSGunnar Mills     for (auto iter = assocs.begin(); iter != assocs.end();)
548ded875dcSGunnar Mills     {
54988e8a325SGunnar Mills         // Since there could be multiple associations to the same path,
55088e8a325SGunnar Mills         // only remove ones that have an active forward association.
55188e8a325SGunnar Mills         if ((std::get<0>(*iter)).compare(ACTIVE_FWD_ASSOCIATION) == 0 &&
55288e8a325SGunnar Mills             (std::get<2>(*iter)).compare(path) == 0)
553ded875dcSGunnar Mills         {
554ded875dcSGunnar Mills             iter = assocs.erase(iter);
555ded875dcSGunnar Mills             associations(assocs);
556ded875dcSGunnar Mills         }
557ded875dcSGunnar Mills         else
558ded875dcSGunnar Mills         {
559ded875dcSGunnar Mills             ++iter;
560ded875dcSGunnar Mills         }
561ded875dcSGunnar Mills     }
562ded875dcSGunnar Mills }
563ded875dcSGunnar Mills 
564b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value)
565b9da6634SSaqib Khan {
566b9da6634SSaqib Khan     for (const auto& intf : activations)
567b9da6634SSaqib Khan     {
568b9da6634SSaqib Khan         if (intf.second->redundancyPriority)
569b9da6634SSaqib Khan         {
570b9da6634SSaqib Khan             if (intf.second->redundancyPriority.get()->priority() < value)
571b9da6634SSaqib Khan             {
572b9da6634SSaqib Khan                 return false;
573b9da6634SSaqib Khan             }
574b9da6634SSaqib Khan         }
575b9da6634SSaqib Khan     }
576b9da6634SSaqib Khan     return true;
577b9da6634SSaqib Khan }
578b9da6634SSaqib Khan 
57949446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars()
58049446ae9SSaqib Khan {
58149446ae9SSaqib Khan     decltype(activations.begin()->second->redundancyPriority.get()->priority())
58249446ae9SSaqib Khan              lowestPriority = std::numeric_limits<uint8_t>::max();
58349446ae9SSaqib Khan     decltype(activations.begin()->second->versionId) lowestPriorityVersion;
58449446ae9SSaqib Khan     for (const auto& intf : activations)
58549446ae9SSaqib Khan     {
58649446ae9SSaqib Khan         if (!intf.second->redundancyPriority.get())
58749446ae9SSaqib Khan         {
58849446ae9SSaqib Khan             // Skip this version if the redundancyPriority is not initialized.
58949446ae9SSaqib Khan             continue;
59049446ae9SSaqib Khan         }
59149446ae9SSaqib Khan 
59249446ae9SSaqib Khan         if (intf.second->redundancyPriority.get()->priority()
59349446ae9SSaqib Khan             <= lowestPriority)
59449446ae9SSaqib Khan         {
59549446ae9SSaqib Khan             lowestPriority = intf.second->redundancyPriority.get()->priority();
59649446ae9SSaqib Khan             lowestPriorityVersion = intf.second->versionId;
59749446ae9SSaqib Khan         }
59849446ae9SSaqib Khan     }
59949446ae9SSaqib Khan 
600f0382c35SSaqib Khan     // Update the U-boot environment variable to point to the lowest priority
601f0382c35SSaqib Khan     auto it = activations.find(lowestPriorityVersion);
602f0382c35SSaqib Khan     it->second->updateUbootEnvVars();
60349446ae9SSaqib Khan }
60449446ae9SSaqib Khan 
605ec1b41c4SGunnar Mills } // namespace updater
606ec1b41c4SGunnar Mills } // namespace software
607ec1b41c4SGunnar Mills } // namespace phosphor
608