135e83f3eSSaqib Khan #include <fstream>
2b77551cdSAdriana Kobylak #include <set>
3ec1b41c4SGunnar Mills #include <string>
42ce7da29SGunnar Mills #include <phosphor-logging/log.hpp>
5dcbfa04aSSaqib Khan #include <phosphor-logging/elog.hpp>
6dcbfa04aSSaqib Khan #include <elog-errors.hpp>
7dcbfa04aSSaqib Khan #include <xyz/openbmc_project/Software/Version/error.hpp>
8ec1b41c4SGunnar Mills #include "config.h"
92ce7da29SGunnar Mills #include "item_updater.hpp"
102ce7da29SGunnar Mills #include "xyz/openbmc_project/Software/Version/server.hpp"
1135e83f3eSSaqib Khan #include <experimental/filesystem>
12705f1bfcSSaqib Khan #include "version.hpp"
135d532675SSaqib Khan #include "serialize.hpp"
14ec1b41c4SGunnar Mills 
15ec1b41c4SGunnar Mills namespace phosphor
16ec1b41c4SGunnar Mills {
17ec1b41c4SGunnar Mills namespace software
18ec1b41c4SGunnar Mills {
19ec1b41c4SGunnar Mills namespace updater
20ec1b41c4SGunnar Mills {
21ec1b41c4SGunnar Mills 
222ce7da29SGunnar Mills // When you see server:: you know we're referencing our base class
232ce7da29SGunnar Mills namespace server = sdbusplus::xyz::openbmc_project::Software::server;
240129d926SMichael Tritz namespace control = sdbusplus::xyz::openbmc_project::Control::server;
252ce7da29SGunnar Mills 
262ce7da29SGunnar Mills using namespace phosphor::logging;
27dcbfa04aSSaqib Khan using namespace sdbusplus::xyz::openbmc_project::Software::Version::Error;
2835e83f3eSSaqib Khan namespace fs = std::experimental::filesystem;
2935e83f3eSSaqib Khan 
30b1cfdf99SMichael Tritz const std::vector<std::string> bmcImages = { "image-kernel",
31b1cfdf99SMichael Tritz                                              "image-rofs",
32b1cfdf99SMichael Tritz                                              "image-rwfs",
33b1cfdf99SMichael Tritz                                              "image-u-boot" };
342ce7da29SGunnar Mills 
35e75d10f5SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message::message& msg)
36ec1b41c4SGunnar Mills {
3784a0e693SSaqib Khan 
3884a0e693SSaqib Khan     using SVersion = server::Version;
3984a0e693SSaqib Khan     using VersionPurpose = SVersion::VersionPurpose;
409a782243SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
4184a0e693SSaqib Khan     namespace mesg = sdbusplus::message;
4284a0e693SSaqib Khan     namespace variant_ns = mesg::variant_ns;
4384a0e693SSaqib Khan 
4484a0e693SSaqib Khan     mesg::object_path objPath;
4584a0e693SSaqib Khan     auto purpose = VersionPurpose::Unknown;
46705f1bfcSSaqib Khan     std::string version;
472ce7da29SGunnar Mills     std::map<std::string,
482ce7da29SGunnar Mills              std::map<std::string,
4984a0e693SSaqib Khan                       mesg::variant<std::string>>> interfaces;
50e75d10f5SPatrick Williams     msg.read(objPath, interfaces);
512ce7da29SGunnar Mills     std::string path(std::move(objPath));
5219177d3eSSaqib Khan     std::string filePath;
532ce7da29SGunnar Mills 
542ce7da29SGunnar Mills     for (const auto& intf : interfaces)
552ce7da29SGunnar Mills     {
56705f1bfcSSaqib Khan         if (intf.first == VERSION_IFACE)
572ce7da29SGunnar Mills         {
582ce7da29SGunnar Mills             for (const auto& property : intf.second)
592ce7da29SGunnar Mills             {
60705f1bfcSSaqib Khan                 if (property.first == "Purpose")
612ce7da29SGunnar Mills                 {
6284a0e693SSaqib Khan                     auto value = SVersion::convertVersionPurposeFromString(
6384a0e693SSaqib Khan                             variant_ns::get<std::string>(property.second));
6484a0e693SSaqib Khan                     if (value == VersionPurpose::BMC ||
6584a0e693SSaqib Khan                         value == VersionPurpose::System)
6684a0e693SSaqib Khan                     {
6784a0e693SSaqib Khan                         purpose = value;
6884a0e693SSaqib Khan                     }
69705f1bfcSSaqib Khan                 }
70705f1bfcSSaqib Khan                 else if (property.first == "Version")
71705f1bfcSSaqib Khan                 {
7284a0e693SSaqib Khan                     version = variant_ns::get<std::string>(property.second);
73705f1bfcSSaqib Khan                 }
74705f1bfcSSaqib Khan             }
75705f1bfcSSaqib Khan         }
7619177d3eSSaqib Khan         else if (intf.first == FILEPATH_IFACE)
7719177d3eSSaqib Khan         {
7819177d3eSSaqib Khan             for (const auto& property : intf.second)
7919177d3eSSaqib Khan             {
8019177d3eSSaqib Khan                 if (property.first == "Path")
8119177d3eSSaqib Khan                 {
8284a0e693SSaqib Khan                     filePath = variant_ns::get<std::string>(property.second);
8319177d3eSSaqib Khan                 }
8419177d3eSSaqib Khan             }
8519177d3eSSaqib Khan         }
86705f1bfcSSaqib Khan     }
87705f1bfcSSaqib Khan     if (version.empty() ||
8819177d3eSSaqib Khan         filePath.empty() ||
8984a0e693SSaqib Khan         purpose == VersionPurpose::Unknown)
902ce7da29SGunnar Mills     {
91e75d10f5SPatrick Williams         return;
922ce7da29SGunnar Mills     }
932ce7da29SGunnar Mills 
942ce7da29SGunnar Mills     // Version id is the last item in the path
952ce7da29SGunnar Mills     auto pos = path.rfind("/");
962ce7da29SGunnar Mills     if (pos == std::string::npos)
972ce7da29SGunnar Mills     {
982ce7da29SGunnar Mills         log<level::ERR>("No version id found in object path",
992ce7da29SGunnar Mills                         entry("OBJPATH=%s", path));
100e75d10f5SPatrick Williams         return;
1012ce7da29SGunnar Mills     }
1022ce7da29SGunnar Mills 
1032ce7da29SGunnar Mills     auto versionId = path.substr(pos + 1);
1042ce7da29SGunnar Mills 
105e75d10f5SPatrick Williams     if (activations.find(versionId) == activations.end())
1062ce7da29SGunnar Mills     {
10735e83f3eSSaqib Khan         // Determine the Activation state by processing the given image dir.
10835e83f3eSSaqib Khan         auto activationState = server::Activation::Activations::Invalid;
1099a782243SGunnar Mills         ItemUpdater::ActivationStatus result =
1109a782243SGunnar Mills                 ItemUpdater::validateSquashFSImage(filePath);
11143b25cdeSGunnar Mills         AssociationList associations = {};
11243b25cdeSGunnar Mills 
11335e83f3eSSaqib Khan         if (result == ItemUpdater::ActivationStatus::ready)
11435e83f3eSSaqib Khan         {
11535e83f3eSSaqib Khan             activationState = server::Activation::Activations::Ready;
116b60add1eSGunnar Mills             // Create an association to the BMC inventory item
11743b25cdeSGunnar Mills             associations.emplace_back(std::make_tuple(
118b60add1eSGunnar Mills                                               ACTIVATION_FWD_ASSOCIATION,
119b60add1eSGunnar Mills                                               ACTIVATION_REV_ASSOCIATION,
12043b25cdeSGunnar Mills                                               bmcInventoryPath));
12143b25cdeSGunnar Mills         }
122b60add1eSGunnar Mills 
123ee13e831SSaqib Khan         activations.insert(std::make_pair(
124ee13e831SSaqib Khan                                    versionId,
125ee13e831SSaqib Khan                                    std::make_unique<Activation>(
12635e83f3eSSaqib Khan                                            bus,
12735e83f3eSSaqib Khan                                            path,
1284c1aec09SSaqib Khan                                            *this,
12935e83f3eSSaqib Khan                                            versionId,
130b60add1eSGunnar Mills                                            activationState,
131ee13e831SSaqib Khan                                            associations)));
1324254beceSMichael Tritz 
133ee13e831SSaqib Khan         auto versionPtr = std::make_unique<VersionClass>(
134705f1bfcSSaqib Khan                                   bus,
135705f1bfcSSaqib Khan                                   path,
136705f1bfcSSaqib Khan                                   version,
137705f1bfcSSaqib Khan                                   purpose,
138ee13e831SSaqib Khan                                   filePath,
139ee13e831SSaqib Khan                                   std::bind(&ItemUpdater::erase,
140ee13e831SSaqib Khan                                             this,
141ee13e831SSaqib Khan                                             std::placeholders::_1));
142ee13e831SSaqib Khan         versionPtr->deleteObject =
143ee13e831SSaqib Khan                 std::make_unique<phosphor::software::manager::Delete>(
144ee13e831SSaqib Khan                         bus, path, *versionPtr);
145ee13e831SSaqib Khan         versions.insert(std::make_pair(versionId, std::move(versionPtr)));
1462ce7da29SGunnar Mills     }
147e75d10f5SPatrick Williams     return;
148ec1b41c4SGunnar Mills }
149ec1b41c4SGunnar Mills 
150ba239881SSaqib Khan void ItemUpdater::processBMCImage()
151ba239881SSaqib Khan {
15288e8a325SGunnar Mills     using VersionClass = phosphor::software::manager::Version;
15388e8a325SGunnar Mills     // Read os-release from /etc/ to get the functional BMC version
15488e8a325SGunnar Mills     auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE);
15588e8a325SGunnar Mills 
1561eef62deSSaqib Khan     // Read os-release from folders under /media/ to get
1571eef62deSSaqib Khan     // BMC Software Versions.
1581eef62deSSaqib Khan     for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
1591eef62deSSaqib Khan     {
1601eef62deSSaqib Khan         auto activationState = server::Activation::Activations::Active;
1616fab70daSSaqib Khan         static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX);
1621eef62deSSaqib Khan 
1631eef62deSSaqib Khan         // Check if the BMC_RO_PREFIXis the prefix of the iter.path
1641eef62deSSaqib Khan         if (0 == iter.path().native().compare(0, BMC_RO_PREFIX_LEN,
1656fab70daSSaqib Khan                                               BMC_ROFS_PREFIX))
1661eef62deSSaqib Khan         {
167021c365bSSaqib Khan             // The versionId is extracted from the path
168021c365bSSaqib Khan             // for example /media/ro-2a1022fe.
169021c365bSSaqib Khan             auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
1701eef62deSSaqib Khan             auto osRelease = iter.path() / OS_RELEASE_FILE;
1711eef62deSSaqib Khan             if (!fs::is_regular_file(osRelease))
1721eef62deSSaqib Khan             {
1732ad1b55fSGunnar Mills                 log<level::ERR>("Failed to read osRelease",
1742ad1b55fSGunnar Mills                                 entry("FILENAME=%s", osRelease.string()));
175021c365bSSaqib Khan                 ItemUpdater::erase(id);
176021c365bSSaqib Khan                 continue;
1771eef62deSSaqib Khan             }
17888e8a325SGunnar Mills             auto version = VersionClass::getBMCVersion(osRelease);
1791eef62deSSaqib Khan             if (version.empty())
1801eef62deSSaqib Khan             {
1811eef62deSSaqib Khan                 log<level::ERR>("Failed to read version from osRelease",
1821eef62deSSaqib Khan                                 entry("FILENAME=%s", osRelease.string()));
1831eef62deSSaqib Khan                 activationState = server::Activation::Activations::Invalid;
1841eef62deSSaqib Khan             }
185021c365bSSaqib Khan 
1861eef62deSSaqib Khan             auto purpose = server::Version::VersionPurpose::BMC;
1871eef62deSSaqib Khan             auto path = fs::path(SOFTWARE_OBJPATH) / id;
1881eef62deSSaqib Khan 
18988e8a325SGunnar Mills             // Create functional association if this is the functional version
19088e8a325SGunnar Mills             if (version.compare(functionalVersion) == 0)
19188e8a325SGunnar Mills             {
19288e8a325SGunnar Mills                 createFunctionalAssociation(path);
19388e8a325SGunnar Mills             }
19488e8a325SGunnar Mills 
19543b25cdeSGunnar Mills             AssociationList associations = {};
19643b25cdeSGunnar Mills 
19743b25cdeSGunnar Mills             if (activationState == server::Activation::Activations::Active)
19843b25cdeSGunnar Mills             {
19943b25cdeSGunnar Mills                 // Create an association to the BMC inventory item
20043b25cdeSGunnar Mills                 associations.emplace_back(std::make_tuple(
20143b25cdeSGunnar Mills                                                   ACTIVATION_FWD_ASSOCIATION,
20243b25cdeSGunnar Mills                                                   ACTIVATION_REV_ASSOCIATION,
20343b25cdeSGunnar Mills                                                   bmcInventoryPath));
20443b25cdeSGunnar Mills 
20543b25cdeSGunnar Mills                 // Create an active association since this image is active
20643b25cdeSGunnar Mills                 createActiveAssociation(path);
20743b25cdeSGunnar Mills             }
20843b25cdeSGunnar Mills 
209ee590c74SAdriana Kobylak             // Create Version instance for this version.
210ee590c74SAdriana Kobylak             auto versionPtr = std::make_unique<VersionClass>(
211ee590c74SAdriana Kobylak                                       bus,
212ee590c74SAdriana Kobylak                                       path,
213ee590c74SAdriana Kobylak                                       version,
214ee590c74SAdriana Kobylak                                       purpose,
215ee13e831SSaqib Khan                                       "",
216ee13e831SSaqib Khan                                       std::bind(&ItemUpdater::erase,
217ee13e831SSaqib Khan                                                 this,
218ee13e831SSaqib Khan                                                 std::placeholders::_1));
219ee590c74SAdriana Kobylak             auto isVersionFunctional = versionPtr->isFunctional();
220ee13e831SSaqib Khan             if (!isVersionFunctional)
221ee13e831SSaqib Khan             {
222ee13e831SSaqib Khan                 versionPtr->deleteObject =
223ee13e831SSaqib Khan                         std::make_unique<phosphor::software::manager::Delete>(
224ee13e831SSaqib Khan                                 bus, path, *versionPtr);
225ee13e831SSaqib Khan             }
226ee590c74SAdriana Kobylak             versions.insert(std::make_pair(
227ee590c74SAdriana Kobylak                                     id,
228ee590c74SAdriana Kobylak                                     std::move(versionPtr)));
229ee590c74SAdriana Kobylak 
2301eef62deSSaqib Khan             // Create Activation instance for this version.
231ee13e831SSaqib Khan             activations.insert(std::make_pair(
232ee13e831SSaqib Khan                                id,
233ee13e831SSaqib Khan                                std::make_unique<Activation>(
234ba239881SSaqib Khan                                         bus,
235ba239881SSaqib Khan                                         path,
2364c1aec09SSaqib Khan                                         *this,
237ba239881SSaqib Khan                                         id,
2384254beceSMichael Tritz                                         activationState,
239ee13e831SSaqib Khan                                         associations)));
2401eef62deSSaqib Khan 
2411eef62deSSaqib Khan             // If Active, create RedundancyPriority instance for this version.
2421eef62deSSaqib Khan             if (activationState == server::Activation::Activations::Active)
2431eef62deSSaqib Khan             {
2441eef62deSSaqib Khan                 uint8_t priority = std::numeric_limits<uint8_t>::max();
2451eef62deSSaqib Khan                 if (!restoreFromFile(id, priority))
2461eef62deSSaqib Khan                 {
247ee590c74SAdriana Kobylak                     if (isVersionFunctional)
248ee590c74SAdriana Kobylak                     {
249ee590c74SAdriana Kobylak                         priority = 0;
250ee590c74SAdriana Kobylak                     }
251ee590c74SAdriana Kobylak                     else
252ee590c74SAdriana Kobylak                     {
2531eef62deSSaqib Khan                         log<level::ERR>("Unable to restore priority from file.",
2541eef62deSSaqib Khan                                 entry("VERSIONID=%s", id));
2551eef62deSSaqib Khan                     }
256ee590c74SAdriana Kobylak                 }
2571eef62deSSaqib Khan                 activations.find(id)->second->redundancyPriority =
2581eef62deSSaqib Khan                         std::make_unique<RedundancyPriority>(
2591eef62deSSaqib Khan                              bus,
2601eef62deSSaqib Khan                              path,
2611eef62deSSaqib Khan                              *(activations.find(id)->second),
262b77551cdSAdriana Kobylak                              priority,
263b77551cdSAdriana Kobylak                              false);
2641eef62deSSaqib Khan             }
2651eef62deSSaqib Khan         }
2661eef62deSSaqib Khan     }
267dcbfa04aSSaqib Khan 
268dcbfa04aSSaqib Khan     // If there is no ubi volume for bmc version then read the /etc/os-release
269dcbfa04aSSaqib Khan     // and create rofs-<versionId> under /media
270dcbfa04aSSaqib Khan     if (activations.size() == 0)
271dcbfa04aSSaqib Khan     {
272d16bcbd5SGunnar Mills         auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE);
273dcbfa04aSSaqib Khan         auto id = phosphor::software::manager::Version::getId(version);
274dcbfa04aSSaqib Khan         auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/";
275dcbfa04aSSaqib Khan         try
276dcbfa04aSSaqib Khan         {
277dcbfa04aSSaqib Khan             if (!fs::is_directory(versionFileDir))
278dcbfa04aSSaqib Khan             {
279dcbfa04aSSaqib Khan                 fs::create_directories(versionFileDir);
280dcbfa04aSSaqib Khan             }
281dcbfa04aSSaqib Khan             auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE;
282dcbfa04aSSaqib Khan             fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath);
283dcbfa04aSSaqib Khan             ItemUpdater::processBMCImage();
284dcbfa04aSSaqib Khan         }
285dcbfa04aSSaqib Khan         catch (const std::exception& e)
286dcbfa04aSSaqib Khan         {
287dcbfa04aSSaqib Khan             log<level::ERR>(e.what());
288dcbfa04aSSaqib Khan         }
289dcbfa04aSSaqib Khan     }
290ba239881SSaqib Khan     return;
291ba239881SSaqib Khan }
292ba239881SSaqib Khan 
2933526ef73SLeonel Gonzalez void ItemUpdater::erase(std::string entryId)
2943526ef73SLeonel Gonzalez {
2956d873715SEddie James     // Find entry in versions map
2966d873715SEddie James     auto it = versions.find(entryId);
2976d873715SEddie James     if (it != versions.end())
2986d873715SEddie James     {
2996d873715SEddie James         if (it->second->isFunctional())
3006d873715SEddie James         {
3016d873715SEddie James             log<level::ERR>(("Error: Version " + entryId + \
3026d873715SEddie James                              " is currently running on the BMC." \
3036d873715SEddie James                              " Unable to remove.").c_str());
3046d873715SEddie James             return;
3056d873715SEddie James         }
3066d873715SEddie James 
3076d873715SEddie James         // Delete ReadOnly partitions if it's not active
3083526ef73SLeonel Gonzalez         removeReadOnlyPartition(entryId);
3091eef62deSSaqib Khan         removeFile(entryId);
310ee13e831SSaqib Khan 
311ee13e831SSaqib Khan         // Removing entry in versions map
312ee13e831SSaqib Khan         this->versions.erase(entryId);
3136d873715SEddie James     }
3146d873715SEddie James     else
3156d873715SEddie James     {
3166d873715SEddie James         // Delete ReadOnly partitions even if we can't find the version
3176d873715SEddie James         removeReadOnlyPartition(entryId);
3186d873715SEddie James         removeFile(entryId);
3196d873715SEddie James 
3206d873715SEddie James         log<level::ERR>(("Error: Failed to find version " + entryId + \
3216d873715SEddie James                          " in item updater versions map." \
3226d873715SEddie James                          " Unable to remove.").c_str());
3236d873715SEddie James     }
3241eef62deSSaqib Khan 
3251eef62deSSaqib Khan     // Remove the priority environment variable.
3261eef62deSSaqib Khan     auto serviceFile = "obmc-flash-bmc-setenv@" + entryId + ".service";
3271eef62deSSaqib Khan     auto method = bus.new_method_call(
3281eef62deSSaqib Khan             SYSTEMD_BUSNAME,
3291eef62deSSaqib Khan             SYSTEMD_PATH,
3301eef62deSSaqib Khan             SYSTEMD_INTERFACE,
3311eef62deSSaqib Khan             "StartUnit");
3321eef62deSSaqib Khan     method.append(serviceFile, "replace");
3331eef62deSSaqib Khan     bus.call_noreply(method);
3343526ef73SLeonel Gonzalez 
3353526ef73SLeonel Gonzalez     // Removing entry in activations map
3363526ef73SLeonel Gonzalez     auto ita = activations.find(entryId);
3373526ef73SLeonel Gonzalez     if (ita == activations.end())
3383526ef73SLeonel Gonzalez     {
3393526ef73SLeonel Gonzalez         log<level::ERR>(("Error: Failed to find version " + entryId + \
3403526ef73SLeonel Gonzalez                          " in item updater activations map." \
3413526ef73SLeonel Gonzalez                          " Unable to remove.").c_str());
3423526ef73SLeonel Gonzalez     }
343ee13e831SSaqib Khan     else
344ee13e831SSaqib Khan     {
3453526ef73SLeonel Gonzalez         this->activations.erase(entryId);
346ee13e831SSaqib Khan     }
34749446ae9SSaqib Khan     ItemUpdater::resetUbootEnvVars();
348ee13e831SSaqib Khan     return;
3493526ef73SLeonel Gonzalez }
3503526ef73SLeonel Gonzalez 
351bc1bf3afSMichael Tritz void ItemUpdater::deleteAll()
352bc1bf3afSMichael Tritz {
353bc1bf3afSMichael Tritz     for (const auto& versionIt : versions)
354bc1bf3afSMichael Tritz     {
355bc1bf3afSMichael Tritz         if (!versionIt.second->isFunctional())
356bc1bf3afSMichael Tritz         {
357ee13e831SSaqib Khan             ItemUpdater::erase(versionIt.first);
358bc1bf3afSMichael Tritz         }
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 {
399b77551cdSAdriana Kobylak     std::map<std::string, uint8_t> priorityMap;
400b77551cdSAdriana Kobylak 
401b77551cdSAdriana Kobylak     // Insert the requested version and priority, it may not exist yet.
402b77551cdSAdriana Kobylak     priorityMap.insert(std::make_pair(versionId, value));
403b77551cdSAdriana Kobylak 
4044c1aec09SSaqib Khan     for (const auto& intf : activations)
4054c1aec09SSaqib Khan     {
4064c1aec09SSaqib Khan         if (intf.second->redundancyPriority)
4074c1aec09SSaqib Khan         {
408b77551cdSAdriana Kobylak             priorityMap.insert(std::make_pair(
409b77551cdSAdriana Kobylak                     intf.first,
410b77551cdSAdriana Kobylak                     intf.second->redundancyPriority.get()->priority()));
411b77551cdSAdriana Kobylak         }
412b77551cdSAdriana Kobylak     }
413b77551cdSAdriana Kobylak 
414b77551cdSAdriana Kobylak     // Lambda function to compare 2 priority values, use <= to allow duplicates
415b77551cdSAdriana Kobylak     typedef std::function<bool(
416b77551cdSAdriana Kobylak             std::pair<std::string, uint8_t>,
417b77551cdSAdriana Kobylak             std::pair<std::string, uint8_t>)> cmpPriority;
418b77551cdSAdriana Kobylak     cmpPriority cmpPriorityFunc = [](
419b77551cdSAdriana Kobylak             std::pair<std::string, uint8_t> priority1,
420b77551cdSAdriana Kobylak             std::pair<std::string, uint8_t> priority2)
4214c1aec09SSaqib Khan     {
422b77551cdSAdriana Kobylak         return priority1.second <= priority2.second;
423b77551cdSAdriana Kobylak     };
424b77551cdSAdriana Kobylak 
425b77551cdSAdriana Kobylak     // Sort versions by ascending priority
426b77551cdSAdriana Kobylak     std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet(
427b77551cdSAdriana Kobylak             priorityMap.begin(), priorityMap.end(), cmpPriorityFunc);
428b77551cdSAdriana Kobylak 
429b77551cdSAdriana Kobylak     auto freePriorityValue = value;
430b77551cdSAdriana Kobylak     for (auto& element : prioritySet)
431b77551cdSAdriana Kobylak     {
432b77551cdSAdriana Kobylak         if (element.first == versionId)
433b77551cdSAdriana Kobylak         {
434b77551cdSAdriana Kobylak             continue;
435b77551cdSAdriana Kobylak         }
436b77551cdSAdriana Kobylak         if (element.second == freePriorityValue)
437b77551cdSAdriana Kobylak         {
438b77551cdSAdriana Kobylak             ++freePriorityValue;
439b77551cdSAdriana Kobylak             auto it = activations.find(element.first);
440b77551cdSAdriana Kobylak             it->second->redundancyPriority.get()->sdbusPriority(
441b77551cdSAdriana Kobylak                     freePriorityValue);
4424c1aec09SSaqib Khan         }
4434c1aec09SSaqib Khan     }
444b77551cdSAdriana Kobylak 
445b77551cdSAdriana Kobylak     auto lowestVersion = prioritySet.begin()->first;
446b77551cdSAdriana Kobylak     if (value == prioritySet.begin()->second)
447b77551cdSAdriana Kobylak     {
448b77551cdSAdriana Kobylak         lowestVersion = versionId;
4494c1aec09SSaqib Khan     }
450b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestVersion);
4514c1aec09SSaqib Khan }
4524c1aec09SSaqib Khan 
45337a59043SMichael Tritz void ItemUpdater::reset()
45437a59043SMichael Tritz {
45537a59043SMichael Tritz     // Mark the read-write partition for recreation upon reboot.
45637a59043SMichael Tritz     auto method = bus.new_method_call(
45737a59043SMichael Tritz             SYSTEMD_BUSNAME,
45837a59043SMichael Tritz             SYSTEMD_PATH,
45937a59043SMichael Tritz             SYSTEMD_INTERFACE,
46037a59043SMichael Tritz             "StartUnit");
4610129d926SMichael Tritz     method.append("obmc-flash-bmc-setenv@rwreset\\x3dtrue.service", "replace");
46237a59043SMichael Tritz     bus.call_noreply(method);
46337a59043SMichael Tritz 
46437a59043SMichael Tritz     log<level::INFO>("BMC factory reset will take effect upon reboot.");
46537a59043SMichael Tritz 
46637a59043SMichael Tritz     return;
46737a59043SMichael Tritz }
46837a59043SMichael Tritz 
4693526ef73SLeonel Gonzalez void ItemUpdater::removeReadOnlyPartition(std::string versionId)
4703526ef73SLeonel Gonzalez {
4713526ef73SLeonel Gonzalez     auto serviceFile = "obmc-flash-bmc-ubiro-remove@" + versionId +
4723526ef73SLeonel Gonzalez             ".service";
4733526ef73SLeonel Gonzalez 
4743526ef73SLeonel Gonzalez     // Remove the read-only partitions.
4753526ef73SLeonel Gonzalez     auto method = bus.new_method_call(
4763526ef73SLeonel Gonzalez             SYSTEMD_BUSNAME,
4773526ef73SLeonel Gonzalez             SYSTEMD_PATH,
4783526ef73SLeonel Gonzalez             SYSTEMD_INTERFACE,
4793526ef73SLeonel Gonzalez             "StartUnit");
4803526ef73SLeonel Gonzalez     method.append(serviceFile, "replace");
4813526ef73SLeonel Gonzalez     bus.call_noreply(method);
4823526ef73SLeonel Gonzalez }
4833526ef73SLeonel Gonzalez 
4840129d926SMichael Tritz bool ItemUpdater::fieldModeEnabled(bool value)
4850129d926SMichael Tritz {
4860129d926SMichael Tritz     // enabling field mode is intended to be one way: false -> true
4870129d926SMichael Tritz     if (value && !control::FieldMode::fieldModeEnabled())
4880129d926SMichael Tritz     {
4890129d926SMichael Tritz         control::FieldMode::fieldModeEnabled(value);
4900129d926SMichael Tritz 
4910129d926SMichael Tritz         auto method = bus.new_method_call(
4920129d926SMichael Tritz                 SYSTEMD_BUSNAME,
4930129d926SMichael Tritz                 SYSTEMD_PATH,
4940129d926SMichael Tritz                 SYSTEMD_INTERFACE,
4950129d926SMichael Tritz                 "StartUnit");
4960129d926SMichael Tritz         method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service",
4970129d926SMichael Tritz                       "replace");
4980129d926SMichael Tritz         bus.call_noreply(method);
4990129d926SMichael Tritz 
5000129d926SMichael Tritz         method = bus.new_method_call(
5010129d926SMichael Tritz                 SYSTEMD_BUSNAME,
5020129d926SMichael Tritz                 SYSTEMD_PATH,
5030129d926SMichael Tritz                 SYSTEMD_INTERFACE,
5040129d926SMichael Tritz                 "StopUnit");
5050129d926SMichael Tritz         method.append("usr-local.mount", "replace");
5060129d926SMichael Tritz         bus.call_noreply(method);
5070129d926SMichael Tritz 
5080129d926SMichael Tritz         std::vector<std::string> usrLocal = {"usr-local.mount"};
5090129d926SMichael Tritz 
5100129d926SMichael Tritz         method = bus.new_method_call(
5110129d926SMichael Tritz                 SYSTEMD_BUSNAME,
5120129d926SMichael Tritz                 SYSTEMD_PATH,
5130129d926SMichael Tritz                 SYSTEMD_INTERFACE,
5140129d926SMichael Tritz                 "MaskUnitFiles");
5150129d926SMichael Tritz         method.append(usrLocal, false, true);
5160129d926SMichael Tritz         bus.call_noreply(method);
5170129d926SMichael Tritz     }
5180129d926SMichael Tritz 
5190129d926SMichael Tritz     return control::FieldMode::fieldModeEnabled();
5200129d926SMichael Tritz }
5210129d926SMichael Tritz 
5220129d926SMichael Tritz void ItemUpdater::restoreFieldModeStatus()
5230129d926SMichael Tritz {
524ff0b421dSMichael Tritz     std::ifstream input("/dev/mtd/u-boot-env");
5250129d926SMichael Tritz     std::string envVar;
5260129d926SMichael Tritz     std::getline(input, envVar);
5270129d926SMichael Tritz 
5280129d926SMichael Tritz     if (envVar.find("fieldmode=true") != std::string::npos)
5290129d926SMichael Tritz     {
5300129d926SMichael Tritz         ItemUpdater::fieldModeEnabled(true);
5310129d926SMichael Tritz     }
5320129d926SMichael Tritz }
5330129d926SMichael Tritz 
534b60add1eSGunnar Mills void ItemUpdater::setBMCInventoryPath()
535b60add1eSGunnar Mills {
536b60add1eSGunnar Mills     auto depth = 0;
537b60add1eSGunnar Mills     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME,
538b60add1eSGunnar Mills                                           MAPPER_PATH,
539b60add1eSGunnar Mills                                           MAPPER_INTERFACE,
540b60add1eSGunnar Mills                                           "GetSubTreePaths");
541b60add1eSGunnar Mills 
542*1254c628SAdriana Kobylak     mapperCall.append(INVENTORY_PATH);
543b60add1eSGunnar Mills     mapperCall.append(depth);
544*1254c628SAdriana Kobylak     std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE};
545b60add1eSGunnar Mills     mapperCall.append(filter);
546b60add1eSGunnar Mills 
547b60add1eSGunnar Mills     auto response = bus.call(mapperCall);
548b60add1eSGunnar Mills     if (response.is_method_error())
549b60add1eSGunnar Mills     {
550b60add1eSGunnar Mills         log<level::ERR>("Error in mapper GetSubTreePath");
551b60add1eSGunnar Mills         return;
552b60add1eSGunnar Mills     }
553b60add1eSGunnar Mills 
554b60add1eSGunnar Mills     using ObjectPaths = std::vector<std::string>;
555b60add1eSGunnar Mills     ObjectPaths result;
556b60add1eSGunnar Mills     response.read(result);
557b60add1eSGunnar Mills 
558*1254c628SAdriana Kobylak     if (!result.empty())
559b60add1eSGunnar Mills     {
560*1254c628SAdriana Kobylak         bmcInventoryPath = result.front();
561b60add1eSGunnar Mills     }
562b60add1eSGunnar Mills 
563b60add1eSGunnar Mills     return;
564b60add1eSGunnar Mills }
565b60add1eSGunnar Mills 
566f10b2326SGunnar Mills void ItemUpdater::createActiveAssociation(const std::string& path)
567ded875dcSGunnar Mills {
568ded875dcSGunnar Mills     assocs.emplace_back(std::make_tuple(ACTIVE_FWD_ASSOCIATION,
569ded875dcSGunnar Mills                                         ACTIVE_REV_ASSOCIATION,
570ded875dcSGunnar Mills                                         path));
571ded875dcSGunnar Mills     associations(assocs);
572ded875dcSGunnar Mills }
573ded875dcSGunnar Mills 
57488e8a325SGunnar Mills void ItemUpdater::createFunctionalAssociation(const std::string& path)
57588e8a325SGunnar Mills {
57688e8a325SGunnar Mills     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
57788e8a325SGunnar Mills                                         FUNCTIONAL_REV_ASSOCIATION,
57888e8a325SGunnar Mills                                         path));
57988e8a325SGunnar Mills     associations(assocs);
58088e8a325SGunnar Mills }
58188e8a325SGunnar Mills 
582f10b2326SGunnar Mills void ItemUpdater::removeActiveAssociation(const std::string& path)
583ded875dcSGunnar Mills {
584ded875dcSGunnar Mills     for (auto iter = assocs.begin(); iter != assocs.end();)
585ded875dcSGunnar Mills     {
58688e8a325SGunnar Mills         // Since there could be multiple associations to the same path,
58788e8a325SGunnar Mills         // only remove ones that have an active forward association.
58888e8a325SGunnar Mills         if ((std::get<0>(*iter)).compare(ACTIVE_FWD_ASSOCIATION) == 0 &&
58988e8a325SGunnar Mills             (std::get<2>(*iter)).compare(path) == 0)
590ded875dcSGunnar Mills         {
591ded875dcSGunnar Mills             iter = assocs.erase(iter);
592ded875dcSGunnar Mills             associations(assocs);
593ded875dcSGunnar Mills         }
594ded875dcSGunnar Mills         else
595ded875dcSGunnar Mills         {
596ded875dcSGunnar Mills             ++iter;
597ded875dcSGunnar Mills         }
598ded875dcSGunnar Mills     }
599ded875dcSGunnar Mills }
600ded875dcSGunnar Mills 
601b9da6634SSaqib Khan bool ItemUpdater::isLowestPriority(uint8_t value)
602b9da6634SSaqib Khan {
603b9da6634SSaqib Khan     for (const auto& intf : activations)
604b9da6634SSaqib Khan     {
605b9da6634SSaqib Khan         if (intf.second->redundancyPriority)
606b9da6634SSaqib Khan         {
607b9da6634SSaqib Khan             if (intf.second->redundancyPriority.get()->priority() < value)
608b9da6634SSaqib Khan             {
609b9da6634SSaqib Khan                 return false;
610b9da6634SSaqib Khan             }
611b9da6634SSaqib Khan         }
612b9da6634SSaqib Khan     }
613b9da6634SSaqib Khan     return true;
614b9da6634SSaqib Khan }
615b9da6634SSaqib Khan 
616b77551cdSAdriana Kobylak void ItemUpdater::updateUbootEnvVars(const std::string& versionId)
617b77551cdSAdriana Kobylak {
618b77551cdSAdriana Kobylak     auto method = bus.new_method_call(
619b77551cdSAdriana Kobylak             SYSTEMD_BUSNAME,
620b77551cdSAdriana Kobylak             SYSTEMD_PATH,
621b77551cdSAdriana Kobylak             SYSTEMD_INTERFACE,
622b77551cdSAdriana Kobylak             "StartUnit");
623b77551cdSAdriana Kobylak     auto updateEnvVarsFile = "obmc-flash-bmc-updateubootvars@" + versionId +
624b77551cdSAdriana Kobylak             ".service";
625b77551cdSAdriana Kobylak     method.append(updateEnvVarsFile, "replace");
626b77551cdSAdriana Kobylak     auto result = bus.call(method);
627b77551cdSAdriana Kobylak 
628b77551cdSAdriana Kobylak     //Check that the bus call didn't result in an error
629b77551cdSAdriana Kobylak     if (result.is_method_error())
630b77551cdSAdriana Kobylak     {
631b77551cdSAdriana Kobylak         log<level::ERR>("Failed to update u-boot env variables",
632b77551cdSAdriana Kobylak                         entry("VERSIONID=%s", versionId));
633b77551cdSAdriana Kobylak     }
634b77551cdSAdriana Kobylak }
635b77551cdSAdriana Kobylak 
63649446ae9SSaqib Khan void ItemUpdater::resetUbootEnvVars()
63749446ae9SSaqib Khan {
63849446ae9SSaqib Khan     decltype(activations.begin()->second->redundancyPriority.get()->priority())
63949446ae9SSaqib Khan              lowestPriority = std::numeric_limits<uint8_t>::max();
64049446ae9SSaqib Khan     decltype(activations.begin()->second->versionId) lowestPriorityVersion;
64149446ae9SSaqib Khan     for (const auto& intf : activations)
64249446ae9SSaqib Khan     {
64349446ae9SSaqib Khan         if (!intf.second->redundancyPriority.get())
64449446ae9SSaqib Khan         {
64549446ae9SSaqib Khan             // Skip this version if the redundancyPriority is not initialized.
64649446ae9SSaqib Khan             continue;
64749446ae9SSaqib Khan         }
64849446ae9SSaqib Khan 
64949446ae9SSaqib Khan         if (intf.second->redundancyPriority.get()->priority()
65049446ae9SSaqib Khan             <= lowestPriority)
65149446ae9SSaqib Khan         {
65249446ae9SSaqib Khan             lowestPriority = intf.second->redundancyPriority.get()->priority();
65349446ae9SSaqib Khan             lowestPriorityVersion = intf.second->versionId;
65449446ae9SSaqib Khan         }
65549446ae9SSaqib Khan     }
65649446ae9SSaqib Khan 
657f0382c35SSaqib Khan     // Update the U-boot environment variable to point to the lowest priority
658b77551cdSAdriana Kobylak     updateUbootEnvVars(lowestPriorityVersion);
65949446ae9SSaqib Khan }
66049446ae9SSaqib Khan 
661ec1b41c4SGunnar Mills } // namespace updater
662ec1b41c4SGunnar Mills } // namespace software
663ec1b41c4SGunnar Mills } // namespace phosphor
664