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 
122*4254beceSMichael Tritz         auto activationPtr = std::make_unique<Activation>(
12335e83f3eSSaqib Khan                 bus,
12435e83f3eSSaqib Khan                 path,
1254c1aec09SSaqib Khan                 *this,
12635e83f3eSSaqib Khan                 versionId,
127b60add1eSGunnar Mills                 activationState,
128*4254beceSMichael Tritz                 associations);
129*4254beceSMichael Tritz 
130*4254beceSMichael Tritz         activationPtr->deleteObject =
131*4254beceSMichael Tritz                 std::make_unique<Delete>(bus, path, *activationPtr);
132*4254beceSMichael Tritz 
133*4254beceSMichael Tritz         activations.insert(std::make_pair(versionId, std::move(activationPtr)));
134*4254beceSMichael Tritz 
135705f1bfcSSaqib Khan         versions.insert(std::make_pair(
136705f1bfcSSaqib Khan                             versionId,
1379a782243SGunnar Mills                             std::make_unique<VersionClass>(
138705f1bfcSSaqib Khan                                 bus,
139705f1bfcSSaqib Khan                                 path,
140705f1bfcSSaqib Khan                                 version,
141705f1bfcSSaqib Khan                                 purpose,
1429440f491SEddie James                                 filePath)));
1432ce7da29SGunnar Mills     }
144e75d10f5SPatrick Williams     return;
145ec1b41c4SGunnar Mills }
146ec1b41c4SGunnar Mills 
147ba239881SSaqib Khan void ItemUpdater::processBMCImage()
148ba239881SSaqib Khan {
14988e8a325SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
15088e8a325SGunnar Mills     // Read os-release from /etc/ to get the functional BMC version
15188e8a325SGunnar Mills     auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE);
15288e8a325SGunnar Mills 
1531eef62deSSaqib Khan     // Read os-release from folders under /media/ to get
1541eef62deSSaqib Khan     // BMC Software Versions.
1551eef62deSSaqib Khan     for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
1561eef62deSSaqib Khan     {
1571eef62deSSaqib Khan         auto activationState = server::Activation::Activations::Active;
1586fab70daSSaqib Khan         static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX);
1591eef62deSSaqib Khan 
1601eef62deSSaqib Khan         // Check if the BMC_RO_PREFIXis the prefix of the iter.path
1611eef62deSSaqib Khan         if (0 == iter.path().native().compare(0, BMC_RO_PREFIX_LEN,
1626fab70daSSaqib Khan                                               BMC_ROFS_PREFIX))
1631eef62deSSaqib Khan         {
164021c365bSSaqib Khan             // The versionId is extracted from the path
165021c365bSSaqib Khan             // for example /media/ro-2a1022fe.
166021c365bSSaqib Khan             auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
1671eef62deSSaqib Khan             auto osRelease = iter.path() / OS_RELEASE_FILE;
1681eef62deSSaqib Khan             if (!fs::is_regular_file(osRelease))
1691eef62deSSaqib Khan             {
1702ad1b55fSGunnar Mills                 log<level::ERR>("Failed to read osRelease",
1712ad1b55fSGunnar Mills                                 entry("FILENAME=%s", osRelease.string()));
172021c365bSSaqib Khan                 ItemUpdater::erase(id);
173021c365bSSaqib Khan                 continue;
1741eef62deSSaqib Khan             }
17588e8a325SGunnar Mills             auto version = VersionClass::getBMCVersion(osRelease);
1761eef62deSSaqib Khan             if (version.empty())
1771eef62deSSaqib Khan             {
1781eef62deSSaqib Khan                 log<level::ERR>("Failed to read version from osRelease",
1791eef62deSSaqib Khan                                 entry("FILENAME=%s", osRelease.string()));
1801eef62deSSaqib Khan                 activationState = server::Activation::Activations::Invalid;
1811eef62deSSaqib Khan             }
182021c365bSSaqib Khan 
1831eef62deSSaqib Khan             auto purpose = server::Version::VersionPurpose::BMC;
1841eef62deSSaqib Khan             auto path = fs::path(SOFTWARE_OBJPATH) / id;
1851eef62deSSaqib Khan 
18688e8a325SGunnar Mills             // Create functional association if this is the functional version
18788e8a325SGunnar Mills             if (version.compare(functionalVersion) == 0)
18888e8a325SGunnar Mills             {
18988e8a325SGunnar Mills                 createFunctionalAssociation(path);
19088e8a325SGunnar Mills             }
19188e8a325SGunnar Mills 
19243b25cdeSGunnar Mills             AssociationList associations = {};
19343b25cdeSGunnar Mills 
19443b25cdeSGunnar Mills             if (activationState == server::Activation::Activations::Active)
19543b25cdeSGunnar Mills             {
19643b25cdeSGunnar Mills                 // Create an association to the BMC inventory item
19743b25cdeSGunnar Mills                 associations.emplace_back(std::make_tuple(
19843b25cdeSGunnar Mills                                                   ACTIVATION_FWD_ASSOCIATION,
19943b25cdeSGunnar Mills                                                   ACTIVATION_REV_ASSOCIATION,
20043b25cdeSGunnar Mills                                                   bmcInventoryPath));
20143b25cdeSGunnar Mills 
20243b25cdeSGunnar Mills                 // Create an active association since this image is active
20343b25cdeSGunnar Mills                 createActiveAssociation(path);
20443b25cdeSGunnar Mills             }
20543b25cdeSGunnar Mills 
206ee590c74SAdriana Kobylak             // Create Version instance for this version.
207ee590c74SAdriana Kobylak             auto versionPtr = std::make_unique<VersionClass>(
208ee590c74SAdriana Kobylak                             bus,
209ee590c74SAdriana Kobylak                             path,
210ee590c74SAdriana Kobylak                             version,
211ee590c74SAdriana Kobylak                             purpose,
212ee590c74SAdriana Kobylak                             "");
213ee590c74SAdriana Kobylak             auto isVersionFunctional = versionPtr->isFunctional();
214ee590c74SAdriana Kobylak             versions.insert(std::make_pair(
215ee590c74SAdriana Kobylak                                 id,
216ee590c74SAdriana Kobylak                                 std::move(versionPtr)));
217ee590c74SAdriana Kobylak 
2181eef62deSSaqib Khan             // Create Activation instance for this version.
219*4254beceSMichael Tritz             auto activationPtr = std::make_unique<Activation>(
220ba239881SSaqib Khan                     bus,
221ba239881SSaqib Khan                     path,
2224c1aec09SSaqib Khan                     *this,
223ba239881SSaqib Khan                     id,
224*4254beceSMichael Tritz                     activationState,
225*4254beceSMichael Tritz                     associations);
226*4254beceSMichael Tritz 
227*4254beceSMichael Tritz             // Add Delete() if this isn't the functional version
228*4254beceSMichael Tritz             if (!isVersionFunctional)
229*4254beceSMichael Tritz             {
230*4254beceSMichael Tritz                 activationPtr->deleteObject =
231*4254beceSMichael Tritz                         std::make_unique<Delete>(bus, path, *activationPtr);
232*4254beceSMichael Tritz             }
233*4254beceSMichael Tritz 
234*4254beceSMichael Tritz             activations.insert(std::make_pair(id, std::move(activationPtr)));
2351eef62deSSaqib Khan 
2361eef62deSSaqib Khan             // If Active, create RedundancyPriority instance for this version.
2371eef62deSSaqib Khan             if (activationState == server::Activation::Activations::Active)
2381eef62deSSaqib Khan             {
2391eef62deSSaqib Khan                 uint8_t priority = std::numeric_limits<uint8_t>::max();
2401eef62deSSaqib Khan                 if (!restoreFromFile(id, priority))
2411eef62deSSaqib Khan                 {
242ee590c74SAdriana Kobylak                     if (isVersionFunctional)
243ee590c74SAdriana Kobylak                     {
244ee590c74SAdriana Kobylak                         priority = 0;
245ee590c74SAdriana Kobylak                     }
246ee590c74SAdriana Kobylak                     else
247ee590c74SAdriana Kobylak                     {
2481eef62deSSaqib Khan                         log<level::ERR>("Unable to restore priority from file.",
2491eef62deSSaqib Khan                                 entry("VERSIONID=%s", id));
2501eef62deSSaqib Khan                     }
251ee590c74SAdriana Kobylak                 }
2521eef62deSSaqib Khan                 activations.find(id)->second->redundancyPriority =
2531eef62deSSaqib Khan                         std::make_unique<RedundancyPriority>(
2541eef62deSSaqib Khan                              bus,
2551eef62deSSaqib Khan                              path,
2561eef62deSSaqib Khan                              *(activations.find(id)->second),
2571eef62deSSaqib Khan                              priority);
2581eef62deSSaqib Khan             }
2591eef62deSSaqib Khan         }
2601eef62deSSaqib Khan     }
261dcbfa04aSSaqib Khan 
262dcbfa04aSSaqib Khan     // If there is no ubi volume for bmc version then read the /etc/os-release
263dcbfa04aSSaqib Khan     // and create rofs-<versionId> under /media
264dcbfa04aSSaqib Khan     if (activations.size() == 0)
265dcbfa04aSSaqib Khan     {
266d16bcbd5SGunnar Mills         auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE);
267dcbfa04aSSaqib Khan         auto id = phosphor::software::manager::Version::getId(version);
268dcbfa04aSSaqib Khan         auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/";
269dcbfa04aSSaqib Khan         try
270dcbfa04aSSaqib Khan         {
271dcbfa04aSSaqib Khan             if (!fs::is_directory(versionFileDir))
272dcbfa04aSSaqib Khan             {
273dcbfa04aSSaqib Khan                 fs::create_directories(versionFileDir);
274dcbfa04aSSaqib Khan             }
275dcbfa04aSSaqib Khan             auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE;
276dcbfa04aSSaqib Khan             fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath);
277dcbfa04aSSaqib Khan             ItemUpdater::processBMCImage();
278dcbfa04aSSaqib Khan         }
279dcbfa04aSSaqib Khan         catch (const std::exception& e)
280dcbfa04aSSaqib Khan         {
281dcbfa04aSSaqib Khan             log<level::ERR>(e.what());
282dcbfa04aSSaqib Khan         }
283dcbfa04aSSaqib Khan     }
284ba239881SSaqib Khan     return;
285ba239881SSaqib Khan }
286ba239881SSaqib Khan 
2873526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId)
2883526ef73SLeonel Gonzalez {
2896d873715SEddie James     // Find entry in versions map
2906d873715SEddie James     auto it = versions.find(entryId);
2916d873715SEddie James     if (it != versions.end())
2926d873715SEddie James     {
2936d873715SEddie James         if (it->second->isFunctional())
2946d873715SEddie James         {
2956d873715SEddie James             log<level::ERR>(("Error: Version " + entryId + \
2966d873715SEddie James                              " is currently running on the BMC." \
2976d873715SEddie James                              " Unable to remove.").c_str());
2986d873715SEddie James             return;
2996d873715SEddie James         }
3006d873715SEddie James 
3016d873715SEddie James         // Delete ReadOnly partitions if it's not active
3023526ef73SLeonel Gonzalez         removeReadOnlyPartition(entryId);
3031eef62deSSaqib Khan         removeFile(entryId);
3046d873715SEddie James     }
3056d873715SEddie James     else
3066d873715SEddie James     {
3076d873715SEddie James         // Delete ReadOnly partitions even if we can't find the version
3086d873715SEddie James         removeReadOnlyPartition(entryId);
3096d873715SEddie James         removeFile(entryId);
3106d873715SEddie James 
3116d873715SEddie James         log<level::ERR>(("Error: Failed to find version " + entryId + \
3126d873715SEddie James                          " in item updater versions map." \
3136d873715SEddie James                          " Unable to remove.").c_str());
3146d873715SEddie James         return;
3156d873715SEddie James     }
3161eef62deSSaqib Khan 
3171eef62deSSaqib Khan     // Remove the priority environment variable.
3181eef62deSSaqib Khan     auto serviceFile = "obmc-flash-bmc-setenv@" + entryId + ".service";
3191eef62deSSaqib Khan     auto method = bus.new_method_call(
3201eef62deSSaqib Khan             SYSTEMD_BUSNAME,
3211eef62deSSaqib Khan             SYSTEMD_PATH,
3221eef62deSSaqib Khan             SYSTEMD_INTERFACE,
3231eef62deSSaqib Khan             "StartUnit");
3241eef62deSSaqib Khan     method.append(serviceFile, "replace");
3251eef62deSSaqib Khan     bus.call_noreply(method);
3263526ef73SLeonel Gonzalez 
3273526ef73SLeonel Gonzalez     // Removing entry in versions map
3283526ef73SLeonel Gonzalez     this->versions.erase(entryId);
3293526ef73SLeonel Gonzalez 
3303526ef73SLeonel Gonzalez     // Removing entry in activations map
3313526ef73SLeonel Gonzalez     auto ita = activations.find(entryId);
3323526ef73SLeonel Gonzalez     if (ita == activations.end())
3333526ef73SLeonel Gonzalez     {
3343526ef73SLeonel Gonzalez         log<level::ERR>(("Error: Failed to find version " + entryId + \
3353526ef73SLeonel Gonzalez                          " in item updater activations map." \
3363526ef73SLeonel Gonzalez                          " Unable to remove.").c_str());
3373526ef73SLeonel Gonzalez         return;
3383526ef73SLeonel Gonzalez     }
3393526ef73SLeonel Gonzalez 
3403526ef73SLeonel Gonzalez     this->activations.erase(entryId);
34149446ae9SSaqib Khan     ItemUpdater::resetUbootEnvVars();
3423526ef73SLeonel Gonzalez }
3433526ef73SLeonel Gonzalez 
344bc1bf3afSMichael Tritz void ItemUpdater::deleteAll()
345bc1bf3afSMichael Tritz {
346bc1bf3afSMichael Tritz     std::vector<std::string> deletableVersions;
347bc1bf3afSMichael Tritz 
348bc1bf3afSMichael Tritz     for (const auto& versionIt : versions)
349bc1bf3afSMichael Tritz     {
350bc1bf3afSMichael Tritz         if (!versionIt.second->isFunctional())
351bc1bf3afSMichael Tritz         {
352bc1bf3afSMichael Tritz             deletableVersions.push_back(versionIt.first);
353bc1bf3afSMichael Tritz         }
354bc1bf3afSMichael Tritz     }
355bc1bf3afSMichael Tritz 
356bc1bf3afSMichael Tritz     for (const auto& deletableIt : deletableVersions)
357bc1bf3afSMichael Tritz     {
358bc1bf3afSMichael Tritz         ItemUpdater::erase(deletableIt);
359bc1bf3afSMichael Tritz     }
360bc1bf3afSMichael Tritz 
361bc1bf3afSMichael Tritz     // Remove any volumes that do not match current versions.
362bc1bf3afSMichael Tritz     auto method = bus.new_method_call(
363bc1bf3afSMichael Tritz             SYSTEMD_BUSNAME,
364bc1bf3afSMichael Tritz             SYSTEMD_PATH,
365bc1bf3afSMichael Tritz             SYSTEMD_INTERFACE,
366bc1bf3afSMichael Tritz             "StartUnit");
367bc1bf3afSMichael Tritz     method.append("obmc-flash-bmc-cleanup.service", "replace");
368bc1bf3afSMichael Tritz     bus.call_noreply(method);
369bc1bf3afSMichael Tritz }
370bc1bf3afSMichael Tritz 
37135e83f3eSSaqib Khan ItemUpdater::ActivationStatus ItemUpdater::validateSquashFSImage(
37219177d3eSSaqib Khan         const std::string& filePath)
37335e83f3eSSaqib Khan {
374b1cfdf99SMichael Tritz     bool invalid = false;
37535e83f3eSSaqib Khan 
376b1cfdf99SMichael Tritz     for (auto& bmcImage : bmcImages)
377b1cfdf99SMichael Tritz     {
37819177d3eSSaqib Khan         fs::path file(filePath);
37935e83f3eSSaqib Khan         file /= bmcImage;
38035e83f3eSSaqib Khan         std::ifstream efile(file.c_str());
381b1cfdf99SMichael Tritz         if (efile.good() != 1)
38235e83f3eSSaqib Khan         {
383b1cfdf99SMichael Tritz             log<level::ERR>("Failed to find the BMC image.",
384b1cfdf99SMichael Tritz                             entry("IMAGE=%s", bmcImage.c_str()));
385b1cfdf99SMichael Tritz             invalid = true;
38635e83f3eSSaqib Khan         }
387b1cfdf99SMichael Tritz     }
388b1cfdf99SMichael Tritz 
389b1cfdf99SMichael Tritz     if (invalid)
39035e83f3eSSaqib Khan     {
39135e83f3eSSaqib Khan         return ItemUpdater::ActivationStatus::invalid;
39235e83f3eSSaqib Khan     }
393b1cfdf99SMichael Tritz 
394b1cfdf99SMichael Tritz     return ItemUpdater::ActivationStatus::ready;
39535e83f3eSSaqib Khan }
39635e83f3eSSaqib Khan 
397b9da6634SSaqib Khan void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
3984c1aec09SSaqib Khan {
3994c1aec09SSaqib Khan     //TODO openbmc/openbmc#1896 Improve the performance of this function
4004c1aec09SSaqib Khan     for (const auto& intf : activations)
4014c1aec09SSaqib Khan     {
4024c1aec09SSaqib Khan         if (intf.second->redundancyPriority)
4034c1aec09SSaqib Khan         {
404b9da6634SSaqib Khan             if (intf.second->redundancyPriority.get()->priority() == value &&
405b9da6634SSaqib Khan                 intf.second->versionId != versionId)
4064c1aec09SSaqib Khan             {
4074c1aec09SSaqib Khan                 intf.second->redundancyPriority.get()->priority(value + 1);
4084c1aec09SSaqib Khan             }
4094c1aec09SSaqib Khan         }
4104c1aec09SSaqib Khan     }
4114c1aec09SSaqib Khan }
4124c1aec09SSaqib Khan 
41337a59043SMichael Tritz void ItemUpdater::reset()
41437a59043SMichael Tritz {
41537a59043SMichael Tritz     // Mark the read-write partition for recreation upon reboot.
41637a59043SMichael Tritz     auto method = bus.new_method_call(
41737a59043SMichael Tritz             SYSTEMD_BUSNAME,
41837a59043SMichael Tritz             SYSTEMD_PATH,
41937a59043SMichael Tritz             SYSTEMD_INTERFACE,
42037a59043SMichael Tritz             "StartUnit");
4210129d926SMichael Tritz     method.append("obmc-flash-bmc-setenv@rwreset\\x3dtrue.service", "replace");
42237a59043SMichael Tritz     bus.call_noreply(method);
42337a59043SMichael Tritz 
42437a59043SMichael Tritz     log<level::INFO>("BMC factory reset will take effect upon reboot.");
42537a59043SMichael Tritz 
42637a59043SMichael Tritz     return;
42737a59043SMichael Tritz }
42837a59043SMichael Tritz 
4293526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId)
4303526ef73SLeonel Gonzalez {
4313526ef73SLeonel Gonzalez     auto serviceFile = "obmc-flash-bmc-ubiro-remove@" + versionId +
4323526ef73SLeonel Gonzalez             ".service";
4333526ef73SLeonel Gonzalez 
4343526ef73SLeonel Gonzalez     // Remove the read-only partitions.
4353526ef73SLeonel Gonzalez     auto method = bus.new_method_call(
4363526ef73SLeonel Gonzalez             SYSTEMD_BUSNAME,
4373526ef73SLeonel Gonzalez             SYSTEMD_PATH,
4383526ef73SLeonel Gonzalez             SYSTEMD_INTERFACE,
4393526ef73SLeonel Gonzalez             "StartUnit");
4403526ef73SLeonel Gonzalez     method.append(serviceFile, "replace");
4413526ef73SLeonel Gonzalez     bus.call_noreply(method);
4423526ef73SLeonel Gonzalez }
4433526ef73SLeonel Gonzalez 
4440129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value)
4450129d926SMichael Tritz {
4460129d926SMichael Tritz     // enabling field mode is intended to be one way: false -> true
4470129d926SMichael Tritz     if (value && !control::FieldMode::fieldModeEnabled())
4480129d926SMichael Tritz     {
4490129d926SMichael Tritz         control::FieldMode::fieldModeEnabled(value);
4500129d926SMichael Tritz 
4510129d926SMichael Tritz         auto method = bus.new_method_call(
4520129d926SMichael Tritz                 SYSTEMD_BUSNAME,
4530129d926SMichael Tritz                 SYSTEMD_PATH,
4540129d926SMichael Tritz                 SYSTEMD_INTERFACE,
4550129d926SMichael Tritz                 "StartUnit");
4560129d926SMichael Tritz         method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service",
4570129d926SMichael Tritz                       "replace");
4580129d926SMichael Tritz         bus.call_noreply(method);
4590129d926SMichael Tritz 
4600129d926SMichael Tritz         method = bus.new_method_call(
4610129d926SMichael Tritz                 SYSTEMD_BUSNAME,
4620129d926SMichael Tritz                 SYSTEMD_PATH,
4630129d926SMichael Tritz                 SYSTEMD_INTERFACE,
4640129d926SMichael Tritz                 "StopUnit");
4650129d926SMichael Tritz         method.append("usr-local.mount", "replace");
4660129d926SMichael Tritz         bus.call_noreply(method);
4670129d926SMichael Tritz 
4680129d926SMichael Tritz         std::vector<std::string> usrLocal = {"usr-local.mount"};
4690129d926SMichael Tritz 
4700129d926SMichael Tritz         method = bus.new_method_call(
4710129d926SMichael Tritz                 SYSTEMD_BUSNAME,
4720129d926SMichael Tritz                 SYSTEMD_PATH,
4730129d926SMichael Tritz                 SYSTEMD_INTERFACE,
4740129d926SMichael Tritz                 "MaskUnitFiles");
4750129d926SMichael Tritz         method.append(usrLocal, false, true);
4760129d926SMichael Tritz         bus.call_noreply(method);
4770129d926SMichael Tritz     }
4780129d926SMichael Tritz 
4790129d926SMichael Tritz     return control::FieldMode::fieldModeEnabled();
4800129d926SMichael Tritz }
4810129d926SMichael Tritz 
4820129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus()
4830129d926SMichael Tritz {
484ff0b421dSMichael Tritz     std::ifstream input("/dev/mtd/u-boot-env");
4850129d926SMichael Tritz     std::string envVar;
4860129d926SMichael Tritz     std::getline(input, envVar);
4870129d926SMichael Tritz 
4880129d926SMichael Tritz     if (envVar.find("fieldmode=true") != std::string::npos)
4890129d926SMichael Tritz     {
4900129d926SMichael Tritz         ItemUpdater::fieldModeEnabled(true);
4910129d926SMichael Tritz     }
4920129d926SMichael Tritz }
4930129d926SMichael Tritz 
494b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath()
495b60add1eSGunnar Mills {
496b60add1eSGunnar Mills     //TODO: openbmc/openbmc#1786 - Get the BMC path by looking for objects
497b60add1eSGunnar Mills     //      that implement the BMC inventory interface
498b60add1eSGunnar Mills     auto depth = 0;
499b60add1eSGunnar Mills     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME,
500b60add1eSGunnar Mills                                           MAPPER_PATH,
501b60add1eSGunnar Mills                                           MAPPER_INTERFACE,
502b60add1eSGunnar Mills                                           "GetSubTreePaths");
503b60add1eSGunnar Mills 
504b60add1eSGunnar Mills     mapperCall.append(CHASSIS_INVENTORY_PATH);
505b60add1eSGunnar Mills     mapperCall.append(depth);
506b60add1eSGunnar Mills 
507b60add1eSGunnar Mills     // TODO: openbmc/openbmc#2226 - Add Inventory Item filter when
508b60add1eSGunnar Mills     //       mapper is fixed.
509b60add1eSGunnar Mills     std::vector<std::string> filter = {};
510b60add1eSGunnar Mills     mapperCall.append(filter);
511b60add1eSGunnar Mills 
512b60add1eSGunnar Mills     auto response = bus.call(mapperCall);
513b60add1eSGunnar Mills     if (response.is_method_error())
514b60add1eSGunnar Mills     {
515b60add1eSGunnar Mills         log<level::ERR>("Error in mapper GetSubTreePath");
516b60add1eSGunnar Mills         return;
517b60add1eSGunnar Mills     }
518b60add1eSGunnar Mills 
519b60add1eSGunnar Mills     using ObjectPaths = std::vector<std::string>;
520b60add1eSGunnar Mills     ObjectPaths result;
521b60add1eSGunnar Mills     response.read(result);
522b60add1eSGunnar Mills 
523b60add1eSGunnar Mills     if (result.empty())
524b60add1eSGunnar Mills     {
525b60add1eSGunnar Mills         log<level::ERR>("Invalid response from mapper");
526b60add1eSGunnar Mills         return;
527b60add1eSGunnar Mills     }
528b60add1eSGunnar Mills 
529b60add1eSGunnar Mills     for (auto& iter : result)
530b60add1eSGunnar Mills     {
531b60add1eSGunnar Mills         const auto& path = iter;
532b60add1eSGunnar Mills         if (path.substr(path.find_last_of('/') + 1).compare("bmc") == 0)
533b60add1eSGunnar Mills         {
534b60add1eSGunnar Mills             bmcInventoryPath = path;
535b60add1eSGunnar Mills             return;
536b60add1eSGunnar Mills         }
537b60add1eSGunnar Mills     }
538b60add1eSGunnar Mills }
539b60add1eSGunnar Mills 
540f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path)
541ded875dcSGunnar Mills {
542ded875dcSGunnar Mills     assocs.emplace_back(std::make_tuple(ACTIVE_FWD_ASSOCIATION,
543ded875dcSGunnar Mills                                         ACTIVE_REV_ASSOCIATION,
544ded875dcSGunnar Mills                                         path));
545ded875dcSGunnar Mills     associations(assocs);
546ded875dcSGunnar Mills }
547ded875dcSGunnar Mills 
54888e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path)
54988e8a325SGunnar Mills {
55088e8a325SGunnar Mills     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
55188e8a325SGunnar Mills                                         FUNCTIONAL_REV_ASSOCIATION,
55288e8a325SGunnar Mills                                         path));
55388e8a325SGunnar Mills     associations(assocs);
55488e8a325SGunnar Mills }
55588e8a325SGunnar Mills 
556f10b2326SGunnar Mills void ItemUpdater::removeActiveAssociation(const std::string& path)
557ded875dcSGunnar Mills {
558ded875dcSGunnar Mills     for (auto iter = assocs.begin(); iter != assocs.end();)
559ded875dcSGunnar Mills     {
56088e8a325SGunnar Mills         // Since there could be multiple associations to the same path,
56188e8a325SGunnar Mills         // only remove ones that have an active forward association.
56288e8a325SGunnar Mills         if ((std::get<0>(*iter)).compare(ACTIVE_FWD_ASSOCIATION) == 0 &&
56388e8a325SGunnar Mills             (std::get<2>(*iter)).compare(path) == 0)
564ded875dcSGunnar Mills         {
565ded875dcSGunnar Mills             iter = assocs.erase(iter);
566ded875dcSGunnar Mills             associations(assocs);
567ded875dcSGunnar Mills         }
568ded875dcSGunnar Mills         else
569ded875dcSGunnar Mills         {
570ded875dcSGunnar Mills             ++iter;
571ded875dcSGunnar Mills         }
572ded875dcSGunnar Mills     }
573ded875dcSGunnar Mills }
574ded875dcSGunnar Mills 
575b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value)
576b9da6634SSaqib Khan {
577b9da6634SSaqib Khan     for (const auto& intf : activations)
578b9da6634SSaqib Khan     {
579b9da6634SSaqib Khan         if (intf.second->redundancyPriority)
580b9da6634SSaqib Khan         {
581b9da6634SSaqib Khan             if (intf.second->redundancyPriority.get()->priority() < value)
582b9da6634SSaqib Khan             {
583b9da6634SSaqib Khan                 return false;
584b9da6634SSaqib Khan             }
585b9da6634SSaqib Khan         }
586b9da6634SSaqib Khan     }
587b9da6634SSaqib Khan     return true;
588b9da6634SSaqib Khan }
589b9da6634SSaqib Khan 
59049446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars()
59149446ae9SSaqib Khan {
59249446ae9SSaqib Khan     decltype(activations.begin()->second->redundancyPriority.get()->priority())
59349446ae9SSaqib Khan              lowestPriority = std::numeric_limits<uint8_t>::max();
59449446ae9SSaqib Khan     decltype(activations.begin()->second->versionId) lowestPriorityVersion;
59549446ae9SSaqib Khan     for (const auto& intf : activations)
59649446ae9SSaqib Khan     {
59749446ae9SSaqib Khan         if (!intf.second->redundancyPriority.get())
59849446ae9SSaqib Khan         {
59949446ae9SSaqib Khan             // Skip this version if the redundancyPriority is not initialized.
60049446ae9SSaqib Khan             continue;
60149446ae9SSaqib Khan         }
60249446ae9SSaqib Khan 
60349446ae9SSaqib Khan         if (intf.second->redundancyPriority.get()->priority()
60449446ae9SSaqib Khan             <= lowestPriority)
60549446ae9SSaqib Khan         {
60649446ae9SSaqib Khan             lowestPriority = intf.second->redundancyPriority.get()->priority();
60749446ae9SSaqib Khan             lowestPriorityVersion = intf.second->versionId;
60849446ae9SSaqib Khan         }
60949446ae9SSaqib Khan     }
61049446ae9SSaqib Khan 
611f0382c35SSaqib Khan     // Update the U-boot environment variable to point to the lowest priority
612f0382c35SSaqib Khan     auto it = activations.find(lowestPriorityVersion);
613f0382c35SSaqib Khan     it->second->updateUbootEnvVars();
61449446ae9SSaqib Khan }
61549446ae9SSaqib Khan 
616ec1b41c4SGunnar Mills } // namespace updater
617ec1b41c4SGunnar Mills } // namespace software
618ec1b41c4SGunnar Mills } // namespace phosphor
619