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 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 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 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 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 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 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 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 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 540 // Removing entry in versions map 541 this->versions.erase(entryId); 542 } 543 544 // Removing entry in updateManagers map 545 auto updateManagerIt = updateManagers.find(entryId); 546 if (updateManagerIt != updateManagers.end()) 547 { 548 updateManagers.erase(entryId); 549 } 550 551 return; 552 } 553 554 void ItemUpdater::deleteAll() 555 { 556 std::vector<std::string> deletableVersions; 557 558 for (const auto& versionIt : versions) 559 { 560 if (!versionIt.second->isFunctional()) 561 { 562 deletableVersions.push_back(versionIt.first); 563 } 564 } 565 566 for (const auto& deletableIt : deletableVersions) 567 { 568 ItemUpdater::erase(deletableIt); 569 } 570 571 helper.cleanup(); 572 } 573 574 ItemUpdater::ActivationStatus ItemUpdater::validateSquashFSImage( 575 const std::string& filePath) 576 { 577 bool valid = true; 578 579 // Record the images which are being updated 580 // First check for the fullimage, then check for images with partitions 581 imageUpdateList.push_back(bmcFullImages); 582 valid = checkImage(filePath, imageUpdateList); 583 if (!valid) 584 { 585 imageUpdateList.clear(); 586 imageUpdateList.assign(bmcImages.begin(), bmcImages.end()); 587 valid = checkImage(filePath, imageUpdateList); 588 if (!valid) 589 { 590 error("Failed to find the needed BMC images."); 591 return ItemUpdater::ActivationStatus::invalid; 592 } 593 } 594 595 return ItemUpdater::ActivationStatus::ready; 596 } 597 598 void ItemUpdater::savePriority(const std::string& versionId, uint8_t value) 599 { 600 auto flashId = versions.find(versionId)->second->path(); 601 storePriority(flashId, value); 602 helper.setEntry(flashId, value); 603 } 604 605 void ItemUpdater::freePriority(uint8_t value, const std::string& versionId) 606 { 607 std::map<std::string, uint8_t> priorityMap; 608 609 // Insert the requested version and priority, it may not exist yet. 610 priorityMap.insert(std::make_pair(versionId, value)); 611 612 for (const auto& intf : activations) 613 { 614 if (intf.second->redundancyPriority) 615 { 616 priorityMap.insert(std::make_pair( 617 intf.first, intf.second->redundancyPriority->priority())); 618 } 619 } 620 621 // Lambda function to compare 2 priority values, use <= to allow duplicates 622 typedef std::function<bool(std::pair<std::string, uint8_t>, 623 std::pair<std::string, uint8_t>)> 624 cmpPriority; 625 cmpPriority cmpPriorityFunc = 626 [](const std::pair<std::string, uint8_t>& priority1, 627 const std::pair<std::string, uint8_t>& priority2) { 628 return priority1.second <= priority2.second; 629 }; 630 631 // Sort versions by ascending priority 632 std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet( 633 priorityMap.begin(), priorityMap.end(), cmpPriorityFunc); 634 635 auto freePriorityValue = value; 636 for (auto& element : prioritySet) 637 { 638 if (element.first == versionId) 639 { 640 continue; 641 } 642 if (element.second == freePriorityValue) 643 { 644 ++freePriorityValue; 645 auto it = activations.find(element.first); 646 it->second->redundancyPriority->sdbusPriority(freePriorityValue); 647 } 648 } 649 650 auto lowestVersion = prioritySet.begin()->first; 651 if (value == prioritySet.begin()->second) 652 { 653 lowestVersion = versionId; 654 } 655 updateUbootEnvVars(lowestVersion); 656 } 657 658 void ItemUpdater::reset() 659 { 660 phosphor::software::updater::Helper::factoryReset(); 661 662 info("BMC factory reset will take effect upon reboot."); 663 } 664 665 void ItemUpdater::removeReadOnlyPartition(const std::string& versionId) 666 { 667 auto flashId = versions.find(versionId)->second->path(); 668 helper.removeVersion(flashId); 669 } 670 671 bool ItemUpdater::fieldModeEnabled(bool value) 672 { 673 // enabling field mode is intended to be one way: false -> true 674 if (value && !control::FieldMode::fieldModeEnabled()) 675 { 676 control::FieldMode::fieldModeEnabled(value); 677 678 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 679 SYSTEMD_INTERFACE, "StartUnit"); 680 method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service", 681 "replace"); 682 bus.call_noreply(method); 683 684 method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 685 SYSTEMD_INTERFACE, "StopUnit"); 686 method.append("usr-local.mount", "replace"); 687 bus.call_noreply(method); 688 689 std::vector<std::string> usrLocal = {"usr-local.mount"}; 690 691 method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 692 SYSTEMD_INTERFACE, "MaskUnitFiles"); 693 method.append(usrLocal, false, true); 694 bus.call_noreply(method); 695 } 696 else if (!value && control::FieldMode::fieldModeEnabled()) 697 { 698 elog<NotAllowed>(xyz::openbmc_project::common::NotAllowed::REASON( 699 "FieldMode is not allowed to be cleared")); 700 } 701 702 return control::FieldMode::fieldModeEnabled(); 703 } 704 705 void ItemUpdater::restoreFieldModeStatus() 706 { 707 // The fieldmode u-boot environment variable may not exist since it is not 708 // part of the default environment, run fw_printenv with 2>&1 to ignore the 709 // error message in the journal "Error: "fieldmode" not defined" 710 std::pair<int, std::string> ret = 711 utils::execute("/sbin/fw_printenv", "-n", "fieldmode", "2>&1"); 712 713 if (ret.first != 0) 714 { 715 return; 716 } 717 718 // truncate any extra characters off the end to compare against a "true" str 719 std::string result = ret.second.substr(0, 4); 720 if (result == "true") 721 { 722 ItemUpdater::fieldModeEnabled(true); 723 } 724 } 725 726 void ItemUpdater::setBMCInventoryPath() 727 { 728 auto depth = 0; 729 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 730 MAPPER_INTERFACE, "GetSubTreePaths"); 731 732 mapperCall.append(INVENTORY_PATH); 733 mapperCall.append(depth); 734 std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE}; 735 mapperCall.append(filter); 736 737 try 738 { 739 auto response = bus.call(mapperCall); 740 741 using ObjectPaths = std::vector<std::string>; 742 ObjectPaths result; 743 response.read(result); 744 745 if (!result.empty()) 746 { 747 bmcInventoryPath = result.front(); 748 } 749 } 750 catch (const sdbusplus::exception_t& e) 751 { 752 error("Error in mapper GetSubTreePath: {ERROR}", "ERROR", e); 753 return; 754 } 755 756 return; 757 } 758 759 void ItemUpdater::createActiveAssociation(const std::string& path) 760 { 761 assocs.emplace_back( 762 std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path)); 763 associations(assocs); 764 } 765 766 void ItemUpdater::createFunctionalAssociation(const std::string& path) 767 { 768 assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION, 769 FUNCTIONAL_REV_ASSOCIATION, path)); 770 associations(assocs); 771 } 772 773 void ItemUpdater::createUpdateableAssociation(const std::string& path) 774 { 775 assocs.emplace_back(std::make_tuple(UPDATEABLE_FWD_ASSOCIATION, 776 UPDATEABLE_REV_ASSOCIATION, path)); 777 associations(assocs); 778 } 779 780 void ItemUpdater::removeAssociations(const std::string& path) 781 { 782 for (auto iter = assocs.begin(); iter != assocs.end();) 783 { 784 if (std::get<2>(*iter) == path) 785 { 786 iter = assocs.erase(iter); 787 associations(assocs); 788 } 789 else 790 { 791 ++iter; 792 } 793 } 794 } 795 796 bool ItemUpdater::isLowestPriority(uint8_t value) 797 { 798 for (const auto& intf : activations) 799 { 800 if (intf.second->redundancyPriority) 801 { 802 if (intf.second->redundancyPriority->priority() < value) 803 { 804 return false; 805 } 806 } 807 } 808 return true; 809 } 810 811 void ItemUpdater::updateUbootEnvVars(const std::string& versionId) 812 { 813 auto it = versions.find(versionId); 814 if (it == versions.end()) 815 { 816 return; 817 } 818 auto flashId = it->second->path(); 819 helper.updateUbootVersionId(flashId); 820 } 821 822 void ItemUpdater::resetUbootEnvVars() 823 { 824 decltype(activations.begin()->second->redundancyPriority->priority()) 825 lowestPriority = std::numeric_limits<uint8_t>::max(); 826 decltype(activations.begin()->second->versionId) lowestPriorityVersion; 827 for (const auto& intf : activations) 828 { 829 if (!intf.second->redundancyPriority) 830 { 831 // Skip this version if the redundancyPriority is not initialized. 832 continue; 833 } 834 835 if (intf.second->redundancyPriority->priority() <= lowestPriority) 836 { 837 lowestPriority = intf.second->redundancyPriority->priority(); 838 lowestPriorityVersion = intf.second->versionId; 839 } 840 } 841 842 // Update the U-boot environment variable to point to the lowest priority 843 updateUbootEnvVars(lowestPriorityVersion); 844 } 845 846 void ItemUpdater::freeSpace([[maybe_unused]] const Activation& caller) 847 { 848 #ifdef BMC_STATIC_DUAL_IMAGE 849 // For the golden image case, always remove the version on the primary side 850 std::string versionIDtoErase; 851 for (const auto& iter : activations) 852 { 853 if (iter.second->redundancyPriority && 854 iter.second->redundancyPriority->priority() == 0) 855 { 856 versionIDtoErase = iter.second->versionId; 857 break; 858 } 859 } 860 if (!versionIDtoErase.empty()) 861 { 862 erase(versionIDtoErase); 863 } 864 else 865 { 866 warning("Failed to find version to erase"); 867 } 868 #else 869 // Versions with the highest priority in front 870 std::priority_queue<std::pair<int, std::string>, 871 std::vector<std::pair<int, std::string>>, 872 std::less<std::pair<int, std::string>>> 873 versionsPQ; 874 875 std::size_t count = 0; 876 for (const auto& iter : activations) 877 { 878 if ((iter.second.get()->activation() == 879 server::Activation::Activations::Active) || 880 (iter.second.get()->activation() == 881 server::Activation::Activations::Failed)) 882 { 883 count++; 884 // Don't put the functional version on the queue since we can't 885 // remove the "running" BMC version. 886 // If ACTIVE_BMC_MAX_ALLOWED <= 1, there is only one active BMC, 887 // so remove functional version as well. 888 // Don't delete the the Activation object that called this function. 889 if ((versions.find(iter.second->versionId) 890 ->second->isFunctional() && 891 ACTIVE_BMC_MAX_ALLOWED > 1) || 892 (iter.second->versionId == caller.versionId)) 893 { 894 continue; 895 } 896 897 // Failed activations don't have priority, assign them a large value 898 // for sorting purposes. 899 auto priority = 999; 900 if (iter.second.get()->activation() == 901 server::Activation::Activations::Active && 902 iter.second->redundancyPriority) 903 { 904 priority = iter.second->redundancyPriority.get()->priority(); 905 } 906 907 versionsPQ.push(std::make_pair(priority, iter.second->versionId)); 908 } 909 } 910 911 // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1, 912 // remove the highest priority one(s). 913 while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty())) 914 { 915 erase(versionsPQ.top().second); 916 versionsPQ.pop(); 917 count--; 918 } 919 #endif 920 } 921 922 void ItemUpdater::mirrorUbootToAlt() 923 { 924 helper.mirrorAlt(); 925 } 926 927 bool ItemUpdater::checkImage(const std::string& filePath, 928 const std::vector<std::string>& imageList) 929 { 930 bool valid = true; 931 932 for (auto& bmcImage : imageList) 933 { 934 fs::path file(filePath); 935 file /= bmcImage; 936 std::ifstream efile(file.c_str()); 937 if (efile.good() != 1) 938 { 939 valid = false; 940 break; 941 } 942 } 943 944 return valid; 945 } 946 947 #ifdef HOST_BIOS_UPGRADE 948 void ItemUpdater::createBIOSObject() 949 { 950 std::string path = BIOS_OBJPATH; 951 // Get version id from last item in the path 952 auto pos = path.rfind('/'); 953 if (pos == std::string::npos) 954 { 955 error("No version id found in object path {PATH}", "PATH", path); 956 return; 957 } 958 959 createActiveAssociation(path); 960 createFunctionalAssociation(path); 961 createUpdateableAssociation(path); 962 963 auto versionId = path.substr(pos + 1); 964 auto version = "null"; 965 AssociationList assocs; 966 biosActivation = std::make_unique<Activation>( 967 ctx, path, *this, versionId, server::Activation::Activations::Active, 968 assocs); 969 auto dummyErase = [](const std::string& /*entryId*/) { 970 // Do nothing; 971 }; 972 biosVersion = std::make_unique<VersionClass>( 973 bus, path, version, VersionPurpose::Host, "", "", 974 std::vector<std::string>(), 975 std::bind(dummyErase, std::placeholders::_1), ""); 976 biosVersion->deleteObject = 977 std::make_unique<phosphor::software::manager::Delete>( 978 bus, path, *biosVersion); 979 980 if (useUpdateDBusInterface) 981 { 982 createUpdateObject(versionId, path); 983 } 984 } 985 #endif 986 987 void ItemUpdater::getRunningSlot() 988 { 989 // Check /run/media/slot to get the slot number 990 constexpr auto slotFile = "/run/media/slot"; 991 std::fstream f(slotFile, std::ios_base::in); 992 f >> runningImageSlot; 993 } 994 995 } // namespace updater 996 } // namespace software 997 } // namespace phosphor 998