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