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