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 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 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 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 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 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 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 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 [](const std::pair<std::string, uint8_t>& priority1, 527 const 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 559 void ItemUpdater::reset() 560 { 561 helper.factoryReset(); 562 563 info("BMC factory reset will take effect upon reboot."); 564 } 565 566 void ItemUpdater::removeReadOnlyPartition(const std::string& versionId) 567 { 568 auto flashId = versions.find(versionId)->second->path(); 569 helper.removeVersion(flashId); 570 } 571 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 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 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 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 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 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 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 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 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 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 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 823 void ItemUpdater::mirrorUbootToAlt() 824 { 825 helper.mirrorAlt(); 826 } 827 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 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 = [](const 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 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