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