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