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