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