phosphor-software-manager: multihost support
-Background: redfish uri FirmwreInventory/bios_active depends on
phosphor-software-manager, and currently it will only show one bios
version in multi-host system due to lack of multihost support.
- Solution: Add compile flag -DMULTIHOST. When specified, it will create
bios_active_1 and bios_active_2 dbus object.
Tested:
```
root ~#: busctl tree xyz.openbmc_project.Software.BMC.Updater
└─ /xyz
└─ /xyz/openbmc_project
└─ /xyz/openbmc_project/software
├─ /xyz/openbmc_project/software/bios_active_1
├─ /xyz/openbmc_project/software/bios_active_2
└─ /xyz/openbmc_project/software/fd5a0214
```
Fusion-Link:
platform5: https://fusion2.corp.google.com/0008dabc-2835-3ea8-8fb3-2f481746726d
platform11: https://fusion2.corp.google.com/fe5f5cc0-fe73-3b32-972c-849bd4b302fa
platform17: https://fusion2.corp.google.com/e6a9339d-d2a4-3372-bc3e-82f5cd249fe8
Google-Bug-Id: 405060721
Change-Id: I1ca41064644b967a8e64d10c4d5d138c5868136d
Signed-off-by: Jimmy Chang <cmchang@google.com>
diff --git a/recipes-phosphor/flash/phosphor-software-manager/0001-Show-two-bios_active-object-in-multihost-system.patch b/recipes-phosphor/flash/phosphor-software-manager/0001-Show-two-bios_active-object-in-multihost-system.patch
new file mode 100644
index 0000000..cc7f8de
--- /dev/null
+++ b/recipes-phosphor/flash/phosphor-software-manager/0001-Show-two-bios_active-object-in-multihost-system.patch
@@ -0,0 +1,159 @@
+From 31eb98d569c6cc21b66922d94e7af66a46bb8753 Mon Sep 17 00:00:00 2001
+From: Jimmy Chang <cmchang@google.com>
+Date: Fri, 7 Nov 2025 08:27:15 +0000
+Subject: [PATCH] Show two bios_active object in multihost system
+
+Add compile flag -DMULTIHOST. When enabled, the phosphor-image-updater
+will generate bios_active_1 and bios_active_2 dbus object. This will
+allow bmcweb to show bios version for multihost system.
+
+Signed-off-by: Jimmy Chang <cmchang@google.com>
+---
+ activation.cpp | 2 +-
+ item_updater.cpp | 63 ++++++++++++++++++++++++++++--------------------
+ item_updater.hpp | 8 +++---
+ meson.build | 1 +
+ meson.options | 5 ++++
+ 5 files changed, 48 insertions(+), 31 deletions(-)
+
+diff --git a/activation.cpp b/activation.cpp
+index 8f4c4c0..04011fd 100644
+--- a/activation.cpp
++++ b/activation.cpp
+@@ -434,7 +434,7 @@ void Activation::onStateChangesBios(sdbusplus::message_t& msg)
+ activation(softwareServer::Activation::Activations::Active);
+
+ info("Bios upgrade completed successfully.");
+- parent.biosVersion->version(
++ parent.biosVersions.at(versionId)->version(
+ parent.versions.find(versionId)->second->version());
+
+ // Delete the uploaded activation
+diff --git a/item_updater.cpp b/item_updater.cpp
+index 9cb9467..ad975d3 100644
+--- a/item_updater.cpp
++++ b/item_updater.cpp
+@@ -848,34 +848,45 @@ bool ItemUpdater::checkImage(const std::string& filePath,
+ #ifdef HOST_BIOS_UPGRADE
+ void ItemUpdater::createBIOSObject()
+ {
+- std::string path = BIOS_OBJPATH;
+- // Get version id from last item in the path
+- auto pos = path.rfind("/");
+- if (pos == std::string::npos)
++#ifdef MULTIHOST
++ const std::vector<std::string> bios_object_paths = {
++ std::string(BIOS_OBJPATH) + "_1", std::string(BIOS_OBJPATH) + "_2"};
++#else
++ const std::vector<std::string> bios_object_paths = {
++ std::string(BIOS_OBJPATH)};
++#endif
++ for (const auto& path : bios_object_paths)
+ {
+- error("No version id found in object path {PATH}", "PATH", path);
+- return;
+- }
+-
+- createActiveAssociation(path);
+- createFunctionalAssociation(path);
++ // Get version id from last item in the path
++ auto pos = path.rfind("/");
++ if (pos == std::string::npos)
++ {
++ error("No version id found in object path {PATH}", "PATH", path);
++ return;
++ }
+
+- auto versionId = path.substr(pos + 1);
+- auto version = "null";
+- AssociationList assocs = {};
+- biosActivation = std::make_unique<Activation>(
+- bus, path, *this, versionId, server::Activation::Activations::Active,
+- assocs);
+- auto dummyErase = [](std::string /*entryId*/) {
+- // Do nothing;
+- };
+- biosVersion = std::make_unique<VersionClass>(
+- bus, path, version, VersionPurpose::Host, "", "",
+- std::vector<std::string>(),
+- std::bind(dummyErase, std::placeholders::_1), "");
+- biosVersion->deleteObject =
+- std::make_unique<phosphor::software::manager::Delete>(bus, path,
+- *biosVersion);
++ createActiveAssociation(path);
++ createFunctionalAssociation(path);
++
++ auto versionId = path.substr(pos + 1);
++ auto version = "null";
++ AssociationList assocs = {};
++ biosActivations.emplace(
++ versionId, std::make_unique<Activation>(
++ bus, path, *this, versionId,
++ server::Activation::Activations::Active, assocs));
++ auto dummyErase = [](std::string /*entryId*/) {
++ // Do nothing;
++ };
++ auto biosVersionPtr = std::make_unique<VersionClass>(
++ bus, path, version, VersionPurpose::Host, "", "",
++ std::vector<std::string>(),
++ std::bind(dummyErase, std::placeholders::_1), "");
++ biosVersionPtr->deleteObject =
++ std::make_unique<phosphor::software::manager::Delete>(
++ bus, path, *biosVersionPtr);
++ biosVersions.emplace(versionId, std::move(biosVersionPtr));
++ }
+ }
+ #endif
+
+diff --git a/item_updater.hpp b/item_updater.hpp
+index 316113a..aea157f 100644
+--- a/item_updater.hpp
++++ b/item_updater.hpp
+@@ -272,12 +272,12 @@ class ItemUpdater : public ItemUpdaterInherit
+ */
+ void createBIOSObject();
+
+- /** @brief Persistent Activation D-Bus object for BIOS */
+- std::unique_ptr<Activation> biosActivation;
++ /** @brief Manages the Activation D-Bus objects for the BIOS */
++ std::map<std::string, std::unique_ptr<Activation>> biosActivations;
+
+ public:
+- /** @brief Persistent Version D-Bus object for BIOS */
+- std::unique_ptr<VersionClass> biosVersion;
++ /** @brief Manages the Version D-Bus objects for the BIOS */
++ std::map<std::string, std::unique_ptr<VersionClass>> biosVersions;
+ #endif
+
+ /** @brief Get the slot number of running image */
+diff --git a/meson.build b/meson.build
+index 4cf01fe..f94962f 100644
+--- a/meson.build
++++ b/meson.build
+@@ -68,6 +68,7 @@ conf.set('MMC_LAYOUT', get_option('bmc-layout').contains('mmc'))
+ # Configurable features
+ conf.set('HOST_BIOS_UPGRADE', get_option('host-bios-upgrade').allowed())
+ conf.set('WANT_SIGNATURE_VERIFY', get_option('verify-signature').allowed())
++conf.set('MULTIHOST', get_option('multihost').allowed())
+
+ # Configurable variables
+ conf.set('ACTIVE_BMC_MAX_ALLOWED', get_option('active-bmc-max-allowed'))
+diff --git a/meson.options b/meson.options
+index b088a8d..cdf1f0c 100644
+--- a/meson.options
++++ b/meson.options
+@@ -30,6 +30,11 @@ option(
+ description: 'Automatic flash side switch on boot',
+ )
+
++option(
++ 'multihost', type: 'feature', value: 'disabled',
++ description: 'Enable multihost system support.',
++)
++
+ # Variables
+ option(
+ 'active-bmc-max-allowed', type: 'integer',
+--
+2.51.2.1041.gc1ab5b90ca-goog
+
diff --git a/recipes-phosphor/flash/phosphor-software-manager_%.bbappend b/recipes-phosphor/flash/phosphor-software-manager_%.bbappend
index d6a370d..4bd3070 100644
--- a/recipes-phosphor/flash/phosphor-software-manager_%.bbappend
+++ b/recipes-phosphor/flash/phosphor-software-manager_%.bbappend
@@ -1,2 +1,9 @@
+FILESEXTRAPATHS:prepend:gbmc := "${THISDIR}/${PN}:"
+
FILES:${PN}-updater += "${systemd_system_unitdir}/obmc-flash-host-bios@.service"
+
+SRC_URI:append:gbmc = " \
+ file://0001-Show-two-bios_active-object-in-multihost-system.patch \
+"
+
EXTRA_OEMESON:append:gbmc = " -Dhost-bios-upgrade=enabled"