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 23 namespace phosphor 24 { 25 namespace software 26 { 27 namespace updater 28 { 29 30 // When you see server:: you know we're referencing our base class 31 namespace server = sdbusplus::xyz::openbmc_project::Software::server; 32 namespace control = sdbusplus::xyz::openbmc_project::Control::server; 33 34 PHOSPHOR_LOG2_USING; 35 using namespace phosphor::logging; 36 using namespace sdbusplus::xyz::openbmc_project::Software::Image::Error; 37 using namespace phosphor::software::image; 38 namespace fs = std::filesystem; 39 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 40 41 void ItemUpdater::createActivation(sdbusplus::message::message& msg) 42 { 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, std::map<std::string, std::variant<std::string>>> 53 interfaces; 54 msg.read(objPath, interfaces); 55 std::string path(std::move(objPath)); 56 std::string filePath; 57 58 for (const auto& intf : interfaces) 59 { 60 if (intf.first == VERSION_IFACE) 61 { 62 for (const auto& property : intf.second) 63 { 64 if (property.first == "Purpose") 65 { 66 auto value = SVersion::convertVersionPurposeFromString( 67 std::get<std::string>(property.second)); 68 if (value == VersionPurpose::BMC || 69 #ifdef HOST_BIOS_UPGRADE 70 value == VersionPurpose::Host || 71 #endif 72 value == VersionPurpose::System) 73 { 74 purpose = value; 75 } 76 } 77 else if (property.first == "Version") 78 { 79 version = std::get<std::string>(property.second); 80 } 81 } 82 } 83 else if (intf.first == FILEPATH_IFACE) 84 { 85 for (const auto& property : intf.second) 86 { 87 if (property.first == "Path") 88 { 89 filePath = std::get<std::string>(property.second); 90 } 91 } 92 } 93 else if (intf.first == EXTENDED_VERSION_IFACE) 94 { 95 for (const auto& property : intf.second) 96 { 97 if (property.first == "ExtendedVersion") 98 { 99 extendedVersion = std::get<std::string>(property.second); 100 } 101 } 102 } 103 } 104 if (version.empty() || filePath.empty() || 105 purpose == VersionPurpose::Unknown) 106 { 107 return; 108 } 109 110 // Version id is the last item in the path 111 auto pos = path.rfind("/"); 112 if (pos == std::string::npos) 113 { 114 error("No version id found in object path: {PATH}", "PATH", path); 115 return; 116 } 117 118 auto versionId = path.substr(pos + 1); 119 120 if (activations.find(versionId) == activations.end()) 121 { 122 // Determine the Activation state by processing the given image dir. 123 auto activationState = server::Activation::Activations::Invalid; 124 ItemUpdater::ActivationStatus result; 125 if (purpose == VersionPurpose::BMC || purpose == VersionPurpose::System) 126 result = ItemUpdater::validateSquashFSImage(filePath); 127 else 128 result = ItemUpdater::ActivationStatus::ready; 129 130 AssociationList associations = {}; 131 132 if (result == ItemUpdater::ActivationStatus::ready) 133 { 134 activationState = server::Activation::Activations::Ready; 135 // Create an association to the BMC inventory item 136 associations.emplace_back( 137 std::make_tuple(ACTIVATION_FWD_ASSOCIATION, 138 ACTIVATION_REV_ASSOCIATION, bmcInventoryPath)); 139 } 140 141 activations.insert(std::make_pair( 142 versionId, 143 std::make_unique<Activation>(bus, path, *this, versionId, 144 activationState, associations))); 145 146 auto versionPtr = std::make_unique<VersionClass>( 147 bus, path, version, purpose, extendedVersion, filePath, 148 std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); 149 versionPtr->deleteObject = 150 std::make_unique<phosphor::software::manager::Delete>(bus, path, 151 *versionPtr); 152 versions.insert(std::make_pair(versionId, std::move(versionPtr))); 153 } 154 return; 155 } 156 157 void ItemUpdater::processBMCImage() 158 { 159 using VersionClass = phosphor::software::manager::Version; 160 161 // Check MEDIA_DIR and create if it does not exist 162 try 163 { 164 if (!fs::is_directory(MEDIA_DIR)) 165 { 166 fs::create_directory(MEDIA_DIR); 167 } 168 } 169 catch (const fs::filesystem_error& e) 170 { 171 error("Failed to prepare dir: {ERROR}", "ERROR", e); 172 return; 173 } 174 175 // Read os-release from /etc/ to get the functional BMC version 176 auto functionalVersion = VersionClass::getBMCVersion(OS_RELEASE_FILE); 177 178 // Read os-release from folders under /media/ to get 179 // BMC Software Versions. 180 for (const auto& iter : fs::directory_iterator(MEDIA_DIR)) 181 { 182 auto activationState = server::Activation::Activations::Active; 183 static const auto BMC_RO_PREFIX_LEN = strlen(BMC_ROFS_PREFIX); 184 185 // Check if the BMC_RO_PREFIXis the prefix of the iter.path 186 if (0 == 187 iter.path().native().compare(0, BMC_RO_PREFIX_LEN, BMC_ROFS_PREFIX)) 188 { 189 // Get the version to calculate the id 190 fs::path releaseFile(OS_RELEASE_FILE); 191 auto osRelease = iter.path() / releaseFile.relative_path(); 192 if (!fs::is_regular_file(osRelease)) 193 { 194 error("Failed to read osRelease: {PATH}", "PATH", osRelease); 195 196 // Try to get the version id from the mount directory name and 197 // call to delete it as this version may be corrupted. Dynamic 198 // volumes created by the UBI layout for example have the id in 199 // the mount directory name. The worst that can happen is that 200 // erase() is called with an non-existent id and returns. 201 auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN); 202 ItemUpdater::erase(id); 203 204 continue; 205 } 206 auto version = VersionClass::getBMCVersion(osRelease); 207 if (version.empty()) 208 { 209 error("Failed to read version from osRelease: {PATH}", "PATH", 210 osRelease); 211 212 // Try to delete the version, same as above if the 213 // OS_RELEASE_FILE does not exist. 214 auto id = iter.path().native().substr(BMC_RO_PREFIX_LEN); 215 ItemUpdater::erase(id); 216 217 continue; 218 } 219 220 auto id = VersionClass::getId(version); 221 222 // Check if the id has already been added. This can happen if the 223 // BMC partitions / devices were manually flashed with the same 224 // image. 225 if (versions.find(id) != versions.end()) 226 { 227 continue; 228 } 229 230 auto purpose = server::Version::VersionPurpose::BMC; 231 restorePurpose(id, purpose); 232 233 // Read os-release from /etc/ to get the BMC extended version 234 std::string extendedVersion = 235 VersionClass::getBMCExtendedVersion(osRelease); 236 237 auto path = fs::path(SOFTWARE_OBJPATH) / id; 238 239 // Create functional association if this is the functional 240 // version 241 if (version.compare(functionalVersion) == 0) 242 { 243 createFunctionalAssociation(path); 244 } 245 246 AssociationList associations = {}; 247 248 if (activationState == server::Activation::Activations::Active) 249 { 250 // Create an association to the BMC inventory item 251 associations.emplace_back(std::make_tuple( 252 ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION, 253 bmcInventoryPath)); 254 255 // Create an active association since this image is active 256 createActiveAssociation(path); 257 } 258 259 // All updateable firmware components must expose the updateable 260 // association. 261 createUpdateableAssociation(path); 262 263 // Create Version instance for this version. 264 auto versionPtr = std::make_unique<VersionClass>( 265 bus, path, version, purpose, extendedVersion, "", 266 std::bind(&ItemUpdater::erase, this, std::placeholders::_1)); 267 auto isVersionFunctional = versionPtr->isFunctional(); 268 if (!isVersionFunctional) 269 { 270 versionPtr->deleteObject = 271 std::make_unique<phosphor::software::manager::Delete>( 272 bus, path, *versionPtr); 273 } 274 versions.insert(std::make_pair(id, std::move(versionPtr))); 275 276 // Create Activation instance for this version. 277 activations.insert(std::make_pair( 278 id, std::make_unique<Activation>( 279 bus, path, *this, id, activationState, associations))); 280 281 // If Active, create RedundancyPriority instance for this 282 // version. 283 if (activationState == server::Activation::Activations::Active) 284 { 285 uint8_t priority = std::numeric_limits<uint8_t>::max(); 286 if (!restorePriority(id, priority)) 287 { 288 if (isVersionFunctional) 289 { 290 priority = 0; 291 } 292 else 293 { 294 error( 295 "Unable to restore priority from file for {VERSIONID}", 296 "VERSIONID", id); 297 } 298 } 299 activations.find(id)->second->redundancyPriority = 300 std::make_unique<RedundancyPriority>( 301 bus, path, *(activations.find(id)->second), priority, 302 false); 303 } 304 } 305 } 306 307 // If there are no bmc versions mounted under MEDIA_DIR, then read the 308 // /etc/os-release and create rofs-<versionId> under MEDIA_DIR, then call 309 // again processBMCImage() to create the D-Bus interface for it. 310 if (activations.size() == 0) 311 { 312 auto version = VersionClass::getBMCVersion(OS_RELEASE_FILE); 313 auto id = phosphor::software::manager::Version::getId(version); 314 auto versionFileDir = BMC_ROFS_PREFIX + id + "/etc/"; 315 try 316 { 317 if (!fs::is_directory(versionFileDir)) 318 { 319 fs::create_directories(versionFileDir); 320 } 321 auto versionFilePath = BMC_ROFS_PREFIX + id + OS_RELEASE_FILE; 322 fs::create_directory_symlink(OS_RELEASE_FILE, versionFilePath); 323 ItemUpdater::processBMCImage(); 324 } 325 catch (const std::exception& e) 326 { 327 error("Exception during processing: {ERROR}", "ERROR", e); 328 } 329 } 330 331 mirrorUbootToAlt(); 332 return; 333 } 334 335 void ItemUpdater::erase(std::string entryId) 336 { 337 // Find entry in versions map 338 auto it = versions.find(entryId); 339 if (it != versions.end()) 340 { 341 if (it->second->isFunctional() && ACTIVE_BMC_MAX_ALLOWED > 1) 342 { 343 error( 344 "Version ({VERSIONID}) is currently running on the BMC; unable to remove.", 345 "VERSIONID", entryId); 346 return; 347 } 348 } 349 350 // First call resetUbootEnvVars() so that the BMC points to a valid image to 351 // boot from. If resetUbootEnvVars() is called after the image is actually 352 // deleted from the BMC flash, there'd be a time window where the BMC would 353 // be pointing to a non-existent image to boot from. 354 // Need to remove the entries from the activations map before that call so 355 // that resetUbootEnvVars() doesn't use the version to be deleted. 356 auto iteratorActivations = activations.find(entryId); 357 if (iteratorActivations == activations.end()) 358 { 359 error( 360 "Failed to find version ({VERSIONID}) in item updater activations map; unable to remove.", 361 "VERSIONID", entryId); 362 } 363 else 364 { 365 removeAssociations(iteratorActivations->second->path); 366 iteratorActivations->second->deleteImageManagerObject(); 367 this->activations.erase(entryId); 368 } 369 ItemUpdater::resetUbootEnvVars(); 370 371 if (it != versions.end()) 372 { 373 // Delete ReadOnly partitions if it's not active 374 removeReadOnlyPartition(entryId); 375 removePersistDataDirectory(entryId); 376 377 // Removing entry in versions map 378 this->versions.erase(entryId); 379 } 380 else 381 { 382 // Delete ReadOnly partitions even if we can't find the version 383 removeReadOnlyPartition(entryId); 384 removePersistDataDirectory(entryId); 385 386 error( 387 "Failed to find version ({VERSIONID}) in item updater versions map; unable to remove.", 388 "VERSIONID", entryId); 389 } 390 391 helper.clearEntry(entryId); 392 393 return; 394 } 395 396 void ItemUpdater::deleteAll() 397 { 398 std::vector<std::string> deletableVersions; 399 400 for (const auto& versionIt : versions) 401 { 402 if (!versionIt.second->isFunctional()) 403 { 404 deletableVersions.push_back(versionIt.first); 405 } 406 } 407 408 for (const auto& deletableIt : deletableVersions) 409 { 410 ItemUpdater::erase(deletableIt); 411 } 412 413 helper.cleanup(); 414 } 415 416 ItemUpdater::ActivationStatus 417 ItemUpdater::validateSquashFSImage(const std::string& filePath) 418 { 419 bool valid = true; 420 421 // Record the images which are being updated 422 // First check for the fullimage, then check for images with partitions 423 imageUpdateList.push_back(bmcFullImages); 424 valid = checkImage(filePath, imageUpdateList); 425 if (!valid) 426 { 427 imageUpdateList.clear(); 428 imageUpdateList.assign(bmcImages.begin(), bmcImages.end()); 429 valid = checkImage(filePath, imageUpdateList); 430 if (!valid) 431 { 432 error("Failed to find the needed BMC images."); 433 return ItemUpdater::ActivationStatus::invalid; 434 } 435 } 436 437 return ItemUpdater::ActivationStatus::ready; 438 } 439 440 void ItemUpdater::savePriority(const std::string& versionId, uint8_t value) 441 { 442 storePriority(versionId, value); 443 helper.setEntry(versionId, value); 444 } 445 446 void ItemUpdater::freePriority(uint8_t value, const std::string& versionId) 447 { 448 std::map<std::string, uint8_t> priorityMap; 449 450 // Insert the requested version and priority, it may not exist yet. 451 priorityMap.insert(std::make_pair(versionId, value)); 452 453 for (const auto& intf : activations) 454 { 455 if (intf.second->redundancyPriority) 456 { 457 priorityMap.insert(std::make_pair( 458 intf.first, intf.second->redundancyPriority.get()->priority())); 459 } 460 } 461 462 // Lambda function to compare 2 priority values, use <= to allow duplicates 463 typedef std::function<bool(std::pair<std::string, uint8_t>, 464 std::pair<std::string, uint8_t>)> 465 cmpPriority; 466 cmpPriority cmpPriorityFunc = 467 [](std::pair<std::string, uint8_t> priority1, 468 std::pair<std::string, uint8_t> priority2) { 469 return priority1.second <= priority2.second; 470 }; 471 472 // Sort versions by ascending priority 473 std::set<std::pair<std::string, uint8_t>, cmpPriority> prioritySet( 474 priorityMap.begin(), priorityMap.end(), cmpPriorityFunc); 475 476 auto freePriorityValue = value; 477 for (auto& element : prioritySet) 478 { 479 if (element.first == versionId) 480 { 481 continue; 482 } 483 if (element.second == freePriorityValue) 484 { 485 ++freePriorityValue; 486 auto it = activations.find(element.first); 487 it->second->redundancyPriority.get()->sdbusPriority( 488 freePriorityValue); 489 } 490 } 491 492 auto lowestVersion = prioritySet.begin()->first; 493 if (value == prioritySet.begin()->second) 494 { 495 lowestVersion = versionId; 496 } 497 updateUbootEnvVars(lowestVersion); 498 } 499 500 void ItemUpdater::reset() 501 { 502 helper.factoryReset(); 503 504 info("BMC factory reset will take effect upon reboot."); 505 } 506 507 void ItemUpdater::removeReadOnlyPartition(std::string versionId) 508 { 509 helper.removeVersion(versionId); 510 } 511 512 bool ItemUpdater::fieldModeEnabled(bool value) 513 { 514 // enabling field mode is intended to be one way: false -> true 515 if (value && !control::FieldMode::fieldModeEnabled()) 516 { 517 control::FieldMode::fieldModeEnabled(value); 518 519 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 520 SYSTEMD_INTERFACE, "StartUnit"); 521 method.append("obmc-flash-bmc-setenv@fieldmode\\x3dtrue.service", 522 "replace"); 523 bus.call_noreply(method); 524 525 method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 526 SYSTEMD_INTERFACE, "StopUnit"); 527 method.append("usr-local.mount", "replace"); 528 bus.call_noreply(method); 529 530 std::vector<std::string> usrLocal = {"usr-local.mount"}; 531 532 method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 533 SYSTEMD_INTERFACE, "MaskUnitFiles"); 534 method.append(usrLocal, false, true); 535 bus.call_noreply(method); 536 } 537 else if (!value && control::FieldMode::fieldModeEnabled()) 538 { 539 elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON( 540 "FieldMode is not allowed to be cleared")); 541 } 542 543 return control::FieldMode::fieldModeEnabled(); 544 } 545 546 void ItemUpdater::restoreFieldModeStatus() 547 { 548 std::ifstream input("/dev/mtd/u-boot-env"); 549 std::string envVar; 550 std::getline(input, envVar); 551 552 if (envVar.find("fieldmode=true") != std::string::npos) 553 { 554 ItemUpdater::fieldModeEnabled(true); 555 } 556 } 557 558 void ItemUpdater::setBMCInventoryPath() 559 { 560 auto depth = 0; 561 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 562 MAPPER_INTERFACE, "GetSubTreePaths"); 563 564 mapperCall.append(INVENTORY_PATH); 565 mapperCall.append(depth); 566 std::vector<std::string> filter = {BMC_INVENTORY_INTERFACE}; 567 mapperCall.append(filter); 568 569 try 570 { 571 auto response = bus.call(mapperCall); 572 573 using ObjectPaths = std::vector<std::string>; 574 ObjectPaths result; 575 response.read(result); 576 577 if (!result.empty()) 578 { 579 bmcInventoryPath = result.front(); 580 } 581 } 582 catch (const sdbusplus::exception::exception& e) 583 { 584 error("Error in mapper GetSubTreePath: {ERROR}", "ERROR", e); 585 return; 586 } 587 588 return; 589 } 590 591 void ItemUpdater::createActiveAssociation(const std::string& path) 592 { 593 assocs.emplace_back( 594 std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path)); 595 associations(assocs); 596 } 597 598 void ItemUpdater::createFunctionalAssociation(const std::string& path) 599 { 600 assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION, 601 FUNCTIONAL_REV_ASSOCIATION, path)); 602 associations(assocs); 603 } 604 605 void ItemUpdater::createUpdateableAssociation(const std::string& path) 606 { 607 assocs.emplace_back(std::make_tuple(UPDATEABLE_FWD_ASSOCIATION, 608 UPDATEABLE_REV_ASSOCIATION, path)); 609 associations(assocs); 610 } 611 612 void ItemUpdater::removeAssociations(const std::string& path) 613 { 614 for (auto iter = assocs.begin(); iter != assocs.end();) 615 { 616 if ((std::get<2>(*iter)).compare(path) == 0) 617 { 618 iter = assocs.erase(iter); 619 associations(assocs); 620 } 621 else 622 { 623 ++iter; 624 } 625 } 626 } 627 628 bool ItemUpdater::isLowestPriority(uint8_t value) 629 { 630 for (const auto& intf : activations) 631 { 632 if (intf.second->redundancyPriority) 633 { 634 if (intf.second->redundancyPriority.get()->priority() < value) 635 { 636 return false; 637 } 638 } 639 } 640 return true; 641 } 642 643 void ItemUpdater::updateUbootEnvVars(const std::string& versionId) 644 { 645 helper.updateUbootVersionId(versionId); 646 } 647 648 void ItemUpdater::resetUbootEnvVars() 649 { 650 decltype(activations.begin()->second->redundancyPriority.get()->priority()) 651 lowestPriority = std::numeric_limits<uint8_t>::max(); 652 decltype(activations.begin()->second->versionId) lowestPriorityVersion; 653 for (const auto& intf : activations) 654 { 655 if (!intf.second->redundancyPriority.get()) 656 { 657 // Skip this version if the redundancyPriority is not initialized. 658 continue; 659 } 660 661 if (intf.second->redundancyPriority.get()->priority() <= lowestPriority) 662 { 663 lowestPriority = intf.second->redundancyPriority.get()->priority(); 664 lowestPriorityVersion = intf.second->versionId; 665 } 666 } 667 668 // Update the U-boot environment variable to point to the lowest priority 669 updateUbootEnvVars(lowestPriorityVersion); 670 } 671 672 void ItemUpdater::freeSpace(Activation& caller) 673 { 674 // Versions with the highest priority in front 675 std::priority_queue<std::pair<int, std::string>, 676 std::vector<std::pair<int, std::string>>, 677 std::less<std::pair<int, std::string>>> 678 versionsPQ; 679 680 std::size_t count = 0; 681 for (const auto& iter : activations) 682 { 683 if ((iter.second.get()->activation() == 684 server::Activation::Activations::Active) || 685 (iter.second.get()->activation() == 686 server::Activation::Activations::Failed)) 687 { 688 count++; 689 // Don't put the functional version on the queue since we can't 690 // remove the "running" BMC version. 691 // If ACTIVE_BMC_MAX_ALLOWED <= 1, there is only one active BMC, 692 // so remove functional version as well. 693 // Don't delete the the Activation object that called this function. 694 if ((versions.find(iter.second->versionId) 695 ->second->isFunctional() && 696 ACTIVE_BMC_MAX_ALLOWED > 1) || 697 (iter.second->versionId == caller.versionId)) 698 { 699 continue; 700 } 701 702 // Failed activations don't have priority, assign them a large value 703 // for sorting purposes. 704 auto priority = 999; 705 if (iter.second.get()->activation() == 706 server::Activation::Activations::Active && 707 iter.second->redundancyPriority) 708 { 709 priority = iter.second->redundancyPriority.get()->priority(); 710 } 711 712 versionsPQ.push(std::make_pair(priority, iter.second->versionId)); 713 } 714 } 715 716 // If the number of BMC versions is over ACTIVE_BMC_MAX_ALLOWED -1, 717 // remove the highest priority one(s). 718 while ((count >= ACTIVE_BMC_MAX_ALLOWED) && (!versionsPQ.empty())) 719 { 720 erase(versionsPQ.top().second); 721 versionsPQ.pop(); 722 count--; 723 } 724 } 725 726 void ItemUpdater::mirrorUbootToAlt() 727 { 728 helper.mirrorAlt(); 729 } 730 731 bool ItemUpdater::checkImage(const std::string& filePath, 732 const std::vector<std::string>& imageList) 733 { 734 bool valid = true; 735 736 for (auto& bmcImage : imageList) 737 { 738 fs::path file(filePath); 739 file /= bmcImage; 740 std::ifstream efile(file.c_str()); 741 if (efile.good() != 1) 742 { 743 valid = false; 744 break; 745 } 746 } 747 748 return valid; 749 } 750 751 #ifdef HOST_BIOS_UPGRADE 752 void ItemUpdater::createBIOSObject() 753 { 754 std::string path = BIOS_OBJPATH; 755 // Get version id from last item in the path 756 auto pos = path.rfind("/"); 757 if (pos == std::string::npos) 758 { 759 error("No version id found in object path {PATH}", "PATH", path); 760 return; 761 } 762 763 createActiveAssociation(path); 764 createFunctionalAssociation(path); 765 766 auto versionId = path.substr(pos + 1); 767 auto version = "null"; 768 AssociationList assocs = {}; 769 biosActivation = std::make_unique<Activation>( 770 bus, path, *this, versionId, server::Activation::Activations::Active, 771 assocs); 772 auto dummyErase = [](std::string /*entryId*/) { 773 // Do nothing; 774 }; 775 biosVersion = std::make_unique<VersionClass>( 776 bus, path, version, VersionPurpose::Host, "", "", 777 std::bind(dummyErase, std::placeholders::_1)); 778 biosVersion->deleteObject = 779 std::make_unique<phosphor::software::manager::Delete>(bus, path, 780 *biosVersion); 781 } 782 #endif 783 784 } // namespace updater 785 } // namespace software 786 } // namespace phosphor 787