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