1 #include "config.h"
2 
3 #include "item_updater.hpp"
4 
5 #include "images.hpp"
6 #include "serialize.hpp"
7 #include "version.hpp"
8 #include "xyz/openbmc_project/Software/ExtendedVersion/server.hpp"
9 #include "xyz/openbmc_project/Software/Version/server.hpp"
10 
11 #include <phosphor-logging/elog-errors.hpp>
12 #include <phosphor-logging/elog.hpp>
13 #include <phosphor-logging/lg2.hpp>
14 #include <xyz/openbmc_project/Common/error.hpp>
15 #include <xyz/openbmc_project/Software/Image/error.hpp>
16 
17 #include <filesystem>
18 #include <fstream>
19 #include <queue>
20 #include <set>
21 #include <string>
22 
23 namespace phosphor
24 {
25 namespace software
26 {
27 namespace updater
28 {
29 
30 // When you see server:: you know we're referencing our base class
31 namespace server = sdbusplus::xyz::openbmc_project::Software::server;
32 namespace control = sdbusplus::xyz::openbmc_project::Control::server;
33 
34 PHOSPHOR_LOG2_USING;
35 using namespace phosphor::logging;
36 using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error;
37 using namespace phosphor::software::image;
38 namespace fs = std::filesystem;
39 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
40 
41 void ItemUpdater::createActivation(sdbusplus::message::message& msg)
42 {
43 
44     using SVersion = server::Version;
45     using VersionPurpose = SVersion::VersionPurpose;
46     using VersionClass = phosphor::software::manager::Version;
47 
48     sdbusplus::message::object_path objPath;
49     auto purpose = VersionPurpose::Unknown;
50     std::string extendedVersion;
51     std::string version;
52     std::map<std::string,
53              std::map<std::string,
54                       std::variant<std::string, std::vector<std::string>>>>
55         interfaces;
56     msg.read(objPath, interfaces);
57     std::string path(std::move(objPath));
58     std::string filePath;
59     std::vector<std::string> compatibleNames;
60 
61     for (const auto& intf : interfaces)
62     {
63         if (intf.first == VERSION_IFACE)
64         {
65             for (const auto& property : intf.second)
66             {
67                 if (property.first == "Purpose")
68                 {
69                     auto value = SVersion::convertVersionPurposeFromString(
70                         std::get<std::string>(property.second));
71                     if (value == VersionPurpose::BMC ||
72 #ifdef HOST_BIOS_UPGRADE
73                         value == VersionPurpose::Host ||
74 #endif
75                         value == VersionPurpose::System)
76                     {
77                         purpose = value;
78                     }
79                 }
80                 else if (property.first == "Version")
81                 {
82                     version = std::get<std::string>(property.second);
83                 }
84             }
85         }
86         else if (intf.first == FILEPATH_IFACE)
87         {
88             for (const auto& property : intf.second)
89             {
90                 if (property.first == "Path")
91                 {
92                     filePath = std::get<std::string>(property.second);
93                 }
94             }
95         }
96         else if (intf.first == EXTENDED_VERSION_IFACE)
97         {
98             for (const auto& property : intf.second)
99             {
100                 if (property.first == "ExtendedVersion")
101                 {
102                     extendedVersion = std::get<std::string>(property.second);
103                 }
104             }
105         }
106         else if (intf.first == COMPATIBLE_IFACE)
107         {
108             for (const auto& property : intf.second)
109             {
110                 if (property.first == "Names")
111                 {
112                     compatibleNames =
113                         std::get<std::vector<std::string>>(property.second);
114                 }
115             }
116         }
117     }
118     if (version.empty() || filePath.empty() ||
119         purpose == VersionPurpose::Unknown)
120     {
121         return;
122     }
123 
124     // Version id is the last item in the path
125     auto pos = path.rfind("/");
126     if (pos == std::string::npos)
127     {
128         error("No version id found in object path: {PATH}", "PATH", path);
129         return;
130     }
131 
132     auto versionId = path.substr(pos + 1);
133 
134     if (activations.find(versionId) == activations.end())
135     {
136         // Determine the Activation state by processing the given image dir.
137         auto activationState = server::Activation::Activations::Invalid;
138         ItemUpdater::ActivationStatus result;
139         if (purpose == VersionPurpose::BMC || purpose == VersionPurpose::System)
140             result = ItemUpdater::validateSquashFSImage(filePath);
141         else
142             result = ItemUpdater::ActivationStatus::ready;
143 
144         AssociationList associations = {};
145 
146         if (result == ItemUpdater::ActivationStatus::ready)
147         {
148             activationState = server::Activation::Activations::Ready;
149             // Create an association to the BMC inventory item
150             associations.emplace_back(
151                 std::make_tuple(ACTIVATION_FWD_ASSOCIATION,
152                                 ACTIVATION_REV_ASSOCIATION, bmcInventoryPath));
153         }
154 
155         auto versionPtr = std::make_unique<VersionClass>(
156             bus, path, version, purpose, extendedVersion, filePath,
157             compatibleNames,
158             std::bind(&ItemUpdater::erase, this, std::placeholders::_1),
159             versionId);
160         versionPtr->deleteObject =
161             std::make_unique<phosphor::software::manager::Delete>(bus, path,
162                                                                   *versionPtr);
163         versions.insert(std::make_pair(versionId, std::move(versionPtr)));
164 
165         activations.insert(std::make_pair(
166             versionId,
167             std::make_unique<Activation>(bus, path, *this, versionId,
168                                          activationState, associations)));
169     }
170     return;
171 }
172 
173 void ItemUpdater::processBMCImage()
174 {
175     using VersionClass = phosphor::software::manager::Version;
176 
177     // Check MEDIA_DIR and create if it does not exist
178     try
179     {
180         if (!fs::is_directory(MEDIA_DIR))
181         {
182             fs::create_directory(MEDIA_DIR);
183         }
184     }
185     catch (const fs::filesystem_error& e)
186     {
187         error("Failed to prepare dir: {ERROR}", "ERROR", e);
188         return;
189     }
190 
191     // Functional images are mounted as rofs-<location>-functional
192     constexpr auto functionalSuffix = "-functional";
193     bool functionalFound = false;
194 
195     // Read os-release from folders under /media/ to get
196     // BMC Software Versions.
197     for (const auto& iter : fs::directory_iterator(MEDIA_DIR))
198     {
199         auto activationState = server::Activation::Activations::Active;
200         static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX);
201 
202         // Check if the BMC_RO_PREFIXis the prefix of the iter.path
203         if (0 ==
204             iter.path().native().compare(0, BMC_RO_PREFIX_LEN, BMC_ROFS_PREFIX))
205         {
206             // Get the version to calculate the id
207             fs::path releaseFile(OS_RELEASE_FILE);
208             auto osRelease = iter.path() / releaseFile.relative_path();
209             if (!fs::is_regular_file(osRelease))
210             {
211 #ifdef BMC_STATIC_DUAL_IMAGE
212                 // For dual image, it is possible that the secondary image is
213                 // empty or contains invalid data, ignore such case.
214                 info("Unable to find osRelease: {PATH}", "PATH", osRelease);
215 #else
216                 error("Failed to read osRelease: {PATH}", "PATH", osRelease);
217 
218                 // Try to get the version id from the mount directory name and
219                 // call to delete it as this version may be corrupted. Dynamic
220                 // volumes created by the UBI layout for example have the id in
221                 // the mount directory name. The worst that can happen is that
222                 // erase() is called with an non-existent id and returns.
223                 auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
224                 ItemUpdater::erase(id);
225 #endif
226 
227                 continue;
228             }
229             auto version = VersionClass::getBMCVersion(osRelease);
230             if (version.empty())
231             {
232                 error("Failed to read version from osRelease: {PATH}", "PATH",
233                       osRelease);
234 
235                 // Try to delete the version, same as above if the
236                 // OS_RELEASE_FILE does not exist.
237                 auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN);
238                 ItemUpdater::erase(id);
239 
240                 continue;
241             }
242 
243             // The flash location is part of the mount name: rofs-<location>
244             auto flashId = iter.path().native().substr(BMC_RO_PREFIX_LEN);
245 
246             auto id = VersionClass::getId(version + flashId);
247 
248             // Check if the id has already been added. This can happen if the
249             // BMC partitions / devices were manually flashed with the same
250             // image.
251             if (versions.find(id) != versions.end())
252             {
253                 continue;
254             }
255 
256             auto functional = false;
257             if (iter.path().native().find(functionalSuffix) !=
258                 std::string::npos)
259             {
260                 // Set functional to true and remove the functional suffix
261                 functional = true;
262                 flashId.erase(flashId.length() - strlen(functionalSuffix));
263                 functionalFound = true;
264             }
265 
266             auto purpose = server::Version::VersionPurpose::BMC;
267             restorePurpose(flashId, purpose);
268 
269             // Read os-release from /etc/ to get the BMC extended version
270             std::string extendedVersion =
271                 VersionClass::getBMCExtendedVersion(osRelease);
272 
273             auto path = fs::path(SOFTWARE_OBJPATH) / id;
274 
275             // Create functional association if this is the functional
276             // version
277             if (functional)
278             {
279                 createFunctionalAssociation(path);
280             }
281 
282             AssociationList associations = {};
283 
284             if (activationState == server::Activation::Activations::Active)
285             {
286                 // Create an association to the BMC inventory item
287                 associations.emplace_back(std::make_tuple(
288                     ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
289                     bmcInventoryPath));
290 
291                 // Create an active association since this image is active
292                 createActiveAssociation(path);
293             }
294 
295             // All updateable firmware components must expose the updateable
296             // association.
297             createUpdateableAssociation(path);
298 
299             // Create Version instance for this version.
300             auto versionPtr = std::make_unique<VersionClass>(
301                 bus, path, version, purpose, extendedVersion, flashId,
302                 std::vector<std::string>(),
303                 std::bind(&ItemUpdater::erase, this, std::placeholders::_1),
304                 id);
305             if (functional)
306             {
307                 versionPtr->setFunctional(true);
308             }
309             else
310             {
311                 versionPtr->deleteObject =
312                     std::make_unique<phosphor::software::manager::Delete>(
313                         bus, path, *versionPtr);
314             }
315             versions.insert(std::make_pair(id, std::move(versionPtr)));
316 
317             // Create Activation instance for this version.
318             activations.insert(std::make_pair(
319                 id, std::make_unique<Activation>(
320                         bus, path, *this, id, activationState, associations)));
321 
322 #ifdef BMC_STATIC_DUAL_IMAGE
323             uint8_t priority;
324             if ((functional && (runningImageSlot == 0)) ||
325                 (!functional && (runningImageSlot == 1)))
326             {
327                 priority = 0;
328             }
329             else
330             {
331                 priority = 1;
332             }
333             activations.find(id)->second->redundancyPriority =
334                 std::make_unique<RedundancyPriority>(
335                     bus, path, *(activations.find(id)->second), priority,
336                     false);
337 #else
338             // If Active, create RedundancyPriority instance for this
339             // version.
340             if (activationState == server::Activation::Activations::Active)
341             {
342                 uint8_t priority = std::numeric_limits<uint8_t>::max();
343                 if (!restorePriority(flashId, priority))
344                 {
345                     if (functional)
346                     {
347                         priority = 0;
348                     }
349                     else
350                     {
351                         error(
352                             "Unable to restore priority from file for {VERSIONID}",
353                             "VERSIONID", id);
354                     }
355                 }
356                 activations.find(id)->second->redundancyPriority =
357                     std::make_unique<RedundancyPriority>(
358                         bus, path, *(activations.find(id)->second), priority,
359                         false);
360             }
361 #endif
362         }
363     }
364 
365     if (!functionalFound)
366     {
367         // If there is no functional version found, read the /etc/os-release and
368         // create rofs-<versionId>-functional under MEDIA_DIR, then call again
369         // processBMCImage() to create the D-Bus interface for it.
370         auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE);
371         auto id = phosphor::software::manager::Version::getId(version +
372                                                               functionalSuffix);
373         auto versionFileDir = BMC_ROFS_PREFIX + id + functionalSuffix + "/etc/";
374         try
375         {
376             if (!fs::is_directory(versionFileDir))
377             {
378                 fs::create_directories(versionFileDir);
379             }
380             auto versionFilePath =
381                 BMC_ROFS_PREFIX + id + functionalSuffix + OS_RELEASE_FILE;
382             fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath);
383             ItemUpdater::processBMCImage();
384         }
385         catch (const std::exception& e)
386         {
387             error("Exception during processing: {ERROR}", "ERROR", e);
388         }
389     }
390 
391     mirrorUbootToAlt();
392     return;
393 }
394 
395 void ItemUpdater::erase(std::string entryId)
396 {
397     // Find entry in versions map
398     auto it = versions.find(entryId);
399     if (it != versions.end())
400     {
401         if (it->second->isFunctional() && ACTIVE_BMC_MAX_ALLOWED > 1)
402         {
403             error(
404                 "Version ({VERSIONID}) is currently running on the BMC; unable to remove.",
405                 "VERSIONID", entryId);
406             return;
407         }
408     }
409 
410     // First call resetUbootEnvVars() so that the BMC points to a valid image to
411     // boot from. If resetUbootEnvVars() is called after the image is actually
412     // deleted from the BMC flash, there'd be a time window where the BMC would
413     // be pointing to a non-existent image to boot from.
414     // Need to remove the entries from the activations map before that call so
415     // that resetUbootEnvVars() doesn't use the version to be deleted.
416     auto iteratorActivations = activations.find(entryId);
417     if (iteratorActivations == activations.end())
418     {
419         error(
420             "Failed to find version ({VERSIONID}) in item updater activations map; unable to remove.",
421             "VERSIONID", entryId);
422     }
423     else
424     {
425         removeAssociations(iteratorActivations->second->path);
426         iteratorActivations->second->deleteImageManagerObject();
427         this->activations.erase(entryId);
428     }
429     ItemUpdater::resetUbootEnvVars();
430 
431     if (it != versions.end())
432     {
433         auto flashId = it->second->path();
434 
435         // Delete version data if it has been installed on flash (path is not
436         // the upload directory)
437         if (flashId.find(IMG_UPLOAD_DIR) == std::string::npos)
438         {
439             removeReadOnlyPartition(entryId);
440             removePersistDataDirectory(flashId);
441             helper.clearEntry(flashId);
442         }
443 
444         // Removing entry in versions map
445         this->versions.erase(entryId);
446     }
447 
448     return;
449 }
450 
451 void ItemUpdater::deleteAll()
452 {
453     std::vector<std::string> deletableVersions;
454 
455     for (const auto& versionIt : versions)
456     {
457         if (!versionIt.second->isFunctional())
458         {
459             deletableVersions.push_back(versionIt.first);
460         }
461     }
462 
463     for (const auto& deletableIt : deletableVersions)
464     {
465         ItemUpdater::erase(deletableIt);
466     }
467 
468     helper.cleanup();
469 }
470 
471 ItemUpdater::ActivationStatus
472     ItemUpdater::validateSquashFSImage(const std::string& filePath)
473 {
474     bool valid = true;
475 
476     // Record the images which are being updated
477     // First check for the fullimage, then check for images with partitions
478     imageUpdateList.push_back(bmcFullImages);
479     valid = checkImage(filePath, imageUpdateList);
480     if (!valid)
481     {
482         imageUpdateList.clear();
483         imageUpdateList.assign(bmcImages.begin(), bmcImages.end());
484         valid = checkImage(filePath, imageUpdateList);
485         if (!valid)
486         {
487             error("Failed to find the needed BMC images.");
488             return ItemUpdater::ActivationStatus::invalid;
489         }
490     }
491 
492     return ItemUpdater::ActivationStatus::ready;
493 }
494 
495 void ItemUpdater::savePriority(const std::string& versionId, uint8_t value)
496 {
497     auto flashId = versions.find(versionId)->second->path();
498     storePriority(flashId, value);
499     helper.setEntry(flashId, value);
500 }
501 
502 void ItemUpdater::freePriority(uint8_t value, const std::string& versionId)
503 {
504     std::map<std::string, uint8_t> priorityMap;
505 
506     // Insert the requested version and priority, it may not exist yet.
507     priorityMap.insert(std::make_pair(versionId, value));
508 
509     for (const auto& intf : activations)
510     {
511         if (intf.second->redundancyPriority)
512         {
513             priorityMap.insert(std::make_pair(
514                 intf.first, intf.second->redundancyPriority.get()->priority()));
515         }
516     }
517 
518     // Lambda function to compare 2 priority values, use <= to allow duplicates
519     typedef std::function<bool(std::pair<std::string, uint8_t>,
520                                std::pair<std::string, uint8_t>)>
521         cmpPriority;
522     cmpPriority cmpPriorityFunc =
523         [](std::pair<std::string, uint8_t> priority1,
524            std::pair<std::string, uint8_t> priority2) {
525             return priority1.second <= priority2.second;
526         };
527 
528     // Sort versions by ascending priority
529     std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet(
530         priorityMap.begin(), priorityMap.end(), cmpPriorityFunc);
531 
532     auto freePriorityValue = value;
533     for (auto& element : prioritySet)
534     {
535         if (element.first == versionId)
536         {
537             continue;
538         }
539         if (element.second == freePriorityValue)
540         {
541             ++freePriorityValue;
542             auto it = activations.find(element.first);
543             it->second->redundancyPriority.get()->sdbusPriority(
544                 freePriorityValue);
545         }
546     }
547 
548     auto lowestVersion = prioritySet.begin()->first;
549     if (value == prioritySet.begin()->second)
550     {
551         lowestVersion = versionId;
552     }
553     updateUbootEnvVars(lowestVersion);
554 }
555 
556 void ItemUpdater::reset()
557 {
558     helper.factoryReset();
559 
560     info("BMC factory reset will take effect upon reboot.");
561 }
562 
563 void ItemUpdater::removeReadOnlyPartition(std::string versionId)
564 {
565     auto flashId = versions.find(versionId)->second->path();
566     helper.removeVersion(flashId);
567 }
568 
569 bool ItemUpdater::fieldModeEnabled(bool value)
570 {
571     // enabling field mode is intended to be one way: false -> true
572     if (value && !control::FieldMode::fieldModeEnabled())
573     {
574         control::FieldMode::fieldModeEnabled(value);
575 
576         auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
577                                           SYSTEMD_INTERFACE, "StartUnit");
578         method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service",
579                       "replace");
580         bus.call_noreply(method);
581 
582         method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
583                                      SYSTEMD_INTERFACE, "StopUnit");
584         method.append("usr-local.mount", "replace");
585         bus.call_noreply(method);
586 
587         std::vector<std::string> usrLocal = {"usr-local.mount"};
588 
589         method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
590                                      SYSTEMD_INTERFACE, "MaskUnitFiles");
591         method.append(usrLocal, false, true);
592         bus.call_noreply(method);
593     }
594     else if (!value && control::FieldMode::fieldModeEnabled())
595     {
596         elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON(
597             "FieldMode is not allowed to be cleared"));
598     }
599 
600     return control::FieldMode::fieldModeEnabled();
601 }
602 
603 void ItemUpdater::restoreFieldModeStatus()
604 {
605     std::ifstream input("/dev/mtd/u-boot-env");
606     std::string envVar;
607     std::getline(input, envVar);
608 
609     if (envVar.find("fieldmode=true") != std::string::npos)
610     {
611         ItemUpdater::fieldModeEnabled(true);
612     }
613 }
614 
615 void ItemUpdater::setBMCInventoryPath()
616 {
617     auto depth = 0;
618     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
619                                           MAPPER_INTERFACE, "GetSubTreePaths");
620 
621     mapperCall.append(INVENTORY_PATH);
622     mapperCall.append(depth);
623     std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE};
624     mapperCall.append(filter);
625 
626     try
627     {
628         auto response = bus.call(mapperCall);
629 
630         using ObjectPaths = std::vector<std::string>;
631         ObjectPaths result;
632         response.read(result);
633 
634         if (!result.empty())
635         {
636             bmcInventoryPath = result.front();
637         }
638     }
639     catch (const sdbusplus::exception::exception& e)
640     {
641         error("Error in mapper GetSubTreePath: {ERROR}", "ERROR", e);
642         return;
643     }
644 
645     return;
646 }
647 
648 void ItemUpdater::createActiveAssociation(const std::string& path)
649 {
650     assocs.emplace_back(
651         std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
652     associations(assocs);
653 }
654 
655 void ItemUpdater::createFunctionalAssociation(const std::string& path)
656 {
657     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
658                                         FUNCTIONAL_REV_ASSOCIATION, path));
659     associations(assocs);
660 }
661 
662 void ItemUpdater::createUpdateableAssociation(const std::string& path)
663 {
664     assocs.emplace_back(std::make_tuple(UPDATEABLE_FWD_ASSOCIATION,
665                                         UPDATEABLE_REV_ASSOCIATION, path));
666     associations(assocs);
667 }
668 
669 void ItemUpdater::removeAssociations(const std::string& path)
670 {
671     for (auto iter = assocs.begin(); iter != assocs.end();)
672     {
673         if ((std::get<2>(*iter)).compare(path) == 0)
674         {
675             iter = assocs.erase(iter);
676             associations(assocs);
677         }
678         else
679         {
680             ++iter;
681         }
682     }
683 }
684 
685 bool ItemUpdater::isLowestPriority(uint8_t value)
686 {
687     for (const auto& intf : activations)
688     {
689         if (intf.second->redundancyPriority)
690         {
691             if (intf.second->redundancyPriority.get()->priority() < value)
692             {
693                 return false;
694             }
695         }
696     }
697     return true;
698 }
699 
700 void ItemUpdater::updateUbootEnvVars(const std::string& versionId)
701 {
702     auto it = versions.find(versionId);
703     if (it == versions.end())
704     {
705         return;
706     }
707     auto flashId = it->second->path();
708     helper.updateUbootVersionId(flashId);
709 }
710 
711 void ItemUpdater::resetUbootEnvVars()
712 {
713     decltype(activations.begin()->second->redundancyPriority.get()->priority())
714         lowestPriority = std::numeric_limits<uint8_t>::max();
715     decltype(activations.begin()->second->versionId) lowestPriorityVersion;
716     for (const auto& intf : activations)
717     {
718         if (!intf.second->redundancyPriority.get())
719         {
720             // Skip this version if the redundancyPriority is not initialized.
721             continue;
722         }
723 
724         if (intf.second->redundancyPriority.get()->priority() <= lowestPriority)
725         {
726             lowestPriority = intf.second->redundancyPriority.get()->priority();
727             lowestPriorityVersion = intf.second->versionId;
728         }
729     }
730 
731     // Update the U-boot environment variable to point to the lowest priority
732     updateUbootEnvVars(lowestPriorityVersion);
733 }
734 
735 void ItemUpdater::freeSpace([[maybe_unused]] const Activation& caller)
736 {
737 #ifdef BMC_STATIC_DUAL_IMAGE
738     // For the golden image case, always remove the version on the primary side
739     std::string versionIDtoErase;
740     for (const auto& iter : activations)
741     {
742         if (iter.second->redundancyPriority &&
743             iter.second->redundancyPriority.get()->priority() == 0)
744         {
745             versionIDtoErase = iter.second->versionId;
746             break;
747         }
748     }
749     if (!versionIDtoErase.empty())
750     {
751         erase(versionIDtoErase);
752     }
753     else
754     {
755         warning("Failed to find version to erase");
756     }
757 #else
758     //  Versions with the highest priority in front
759     std::priority_queue<std::pair<int, std::string>,
760                         std::vector<std::pair<int, std::string>>,
761                         std::less<std::pair<int, std::string>>>
762         versionsPQ;
763 
764     std::size_t count = 0;
765     for (const auto& iter : activations)
766     {
767         if ((iter.second.get()->activation() ==
768              server::Activation::Activations::Active) ||
769             (iter.second.get()->activation() ==
770              server::Activation::Activations::Failed))
771         {
772             count++;
773             // Don't put the functional version on the queue since we can't
774             // remove the "running" BMC version.
775             // If ACTIVE_BMC_MAX_ALLOWED <= 1, there is only one active BMC,
776             // so remove functional version as well.
777             // Don't delete the the Activation object that called this function.
778             if ((versions.find(iter.second->versionId)
779                      ->second->isFunctional() &&
780                  ACTIVE_BMC_MAX_ALLOWED > 1) ||
781                 (iter.second->versionId == caller.versionId))
782             {
783                 continue;
784             }
785 
786             // Failed activations don't have priority, assign them a large value
787             // for sorting purposes.
788             auto priority = 999;
789             if (iter.second.get()->activation() ==
790                     server::Activation::Activations::Active &&
791                 iter.second->redundancyPriority)
792             {
793                 priority = iter.second->redundancyPriority.get()->priority();
794             }
795 
796             versionsPQ.push(std::make_pair(priority, iter.second->versionId));
797         }
798     }
799 
800     // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1,
801     // remove the highest priority one(s).
802     while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty()))
803     {
804         erase(versionsPQ.top().second);
805         versionsPQ.pop();
806         count--;
807     }
808 #endif
809 }
810 
811 void ItemUpdater::mirrorUbootToAlt()
812 {
813     helper.mirrorAlt();
814 }
815 
816 bool ItemUpdater::checkImage(const std::string& filePath,
817                              const std::vector<std::string>& imageList)
818 {
819     bool valid = true;
820 
821     for (auto& bmcImage : imageList)
822     {
823         fs::path file(filePath);
824         file /= bmcImage;
825         std::ifstream efile(file.c_str());
826         if (efile.good() != 1)
827         {
828             valid = false;
829             break;
830         }
831     }
832 
833     return valid;
834 }
835 
836 #ifdef HOST_BIOS_UPGRADE
837 void ItemUpdater::createBIOSObject()
838 {
839     std::string path = BIOS_OBJPATH;
840     // Get version id from last item in the path
841     auto pos = path.rfind("/");
842     if (pos == std::string::npos)
843     {
844         error("No version id found in object path {PATH}", "PATH", path);
845         return;
846     }
847 
848     createActiveAssociation(path);
849     createFunctionalAssociation(path);
850 
851     auto versionId = path.substr(pos + 1);
852     auto version = "null";
853     AssociationList assocs = {};
854     biosActivation = std::make_unique<Activation>(
855         bus, path, *this, versionId, server::Activation::Activations::Active,
856         assocs);
857     auto dummyErase = [](std::string /*entryId*/) {
858         // Do nothing;
859     };
860     biosVersion = std::make_unique<VersionClass>(
861         bus, path, version, VersionPurpose::Host, "", "",
862         std::vector<std::string>(),
863         std::bind(dummyErase, std::placeholders::_1), "");
864     biosVersion->deleteObject =
865         std::make_unique<phosphor::software::manager::Delete>(bus, path,
866                                                               *biosVersion);
867 }
868 #endif
869 
870 void ItemUpdater::getRunningSlot()
871 {
872     // Check /run/media/slot to get the slot number
873     constexpr auto slotFile = "/run/media/slot";
874     std::fstream f(slotFile, std::ios_base::in);
875     f >> runningImageSlot;
876 }
877 
878 } // namespace updater
879 } // namespace software
880 } // namespace phosphor
881