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 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 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 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 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 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 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 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 569 void ItemUpdater::reset() 570 { 571 phosphor::software::updater::Helper::factoryReset(); 572 573 info("BMC factory reset will take effect upon reboot."); 574 } 575 576 void ItemUpdater::removeReadOnlyPartition(const std::string& versionId) 577 { 578 auto flashId = versions.find(versionId)->second->path(); 579 helper.removeVersion(flashId); 580 } 581 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 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 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 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 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 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 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 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 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 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 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 833 void ItemUpdater::mirrorUbootToAlt() 834 { 835 helper.mirrorAlt(); 836 } 837 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 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 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