dual-image: Implement BMC update when running on secondary

Implement the BMC code update when it's running on the secondary image.
It will update the alt flash by obmc-flash-bmc-alt@.service and
wait for the completion.
After the update is done, it needs reboot to take effect.

Note if the BMC is running on the secondary, it requires a following
commit to reset the CS to make the SoC to boot from the primary flash.

This commit only flashes the alt image when it's running on the
secondary image, there will be future commits to support other cases,
e.g. flashing both images.

Tested: Verify the code update process is successful and it flashes the
        whole primary chip when the BMC is running on the secondary
        chip.

Signed-off-by: Lei YU <yulei.sh@bytedance.com>
Change-Id: Ifa849e55c28f17b46d7f999ff43a7ad7e73f2ea1
diff --git a/activation.cpp b/activation.cpp
index be781d1..721e2cb 100644
--- a/activation.cpp
+++ b/activation.cpp
@@ -172,9 +172,14 @@
 
 #else // STATIC_LAYOUT
 
-        onFlashWriteSuccess();
-        return softwareServer::Activation::activation(
-            softwareServer::Activation::Activations::Active);
+        if (parent.runningImageSlot == 0)
+        {
+            // On primary, update it as before
+            onFlashWriteSuccess();
+            return softwareServer::Activation::activation(
+                softwareServer::Activation::Activations::Active);
+        }
+        // On secondary, wait for the service to complete
 #endif
     }
     else
diff --git a/item_updater.cpp b/item_updater.cpp
index 5d820d6..cfe0ae5 100644
--- a/item_updater.cpp
+++ b/item_updater.cpp
@@ -716,8 +716,29 @@
     updateUbootEnvVars(lowestPriorityVersion);
 }
 
-void ItemUpdater::freeSpace(const Activation& caller)
+void ItemUpdater::freeSpace([[maybe_unused]] const Activation& caller)
 {
+#ifdef BMC_STATIC_DUAL_IMAGE
+    // For the golden image case, always remove the version on the primary side
+    std::string versionIDtoErase;
+    for (const auto& iter : activations)
+    {
+        if (iter.second->redundancyPriority &&
+            iter.second->redundancyPriority.get()->priority() == 0)
+        {
+            versionIDtoErase = iter.second->versionId;
+            break;
+        }
+    }
+    if (!versionIDtoErase.empty())
+    {
+        erase(versionIDtoErase);
+    }
+    else
+    {
+        warning("Failed to find version to erase");
+    }
+#else
     //  Versions with the highest priority in front
     std::priority_queue<std::pair<int, std::string>,
                         std::vector<std::pair<int, std::string>>,
@@ -768,6 +789,7 @@
         versionsPQ.pop();
         count--;
     }
+#endif
 }
 
 void ItemUpdater::mirrorUbootToAlt()
diff --git a/meson.build b/meson.build
index fffed1a..2e12de7 100644
--- a/meson.build
+++ b/meson.build
@@ -188,6 +188,9 @@
         'static/flash.cpp',
         'static/item_updater_helper.cpp'
     )
+    unit_files += [
+        'static/obmc-flash-bmc-alt@.service.in',
+    ]
 elif get_option('bmc-layout').contains('ubi')
     image_updater_sources += files(
         'ubi/flash.cpp',
diff --git a/static/flash.cpp b/static/flash.cpp
index 101828b..748a6cf 100644
--- a/static/flash.cpp
+++ b/static/flash.cpp
@@ -6,11 +6,14 @@
 #include "images.hpp"
 #include "item_updater.hpp"
 
+#include <phosphor-logging/lg2.hpp>
+
 #include <filesystem>
 
 namespace
 {
 constexpr auto PATH_INITRAMFS = "/run/initramfs";
+constexpr auto FLASH_ALT_SERVICE_TMPL = "obmc-flash-bmc-alt@";
 } // namespace
 
 namespace phosphor
@@ -20,11 +23,27 @@
 namespace updater
 {
 
+PHOSPHOR_LOG2_USING;
+
 namespace fs = std::filesystem;
 using namespace phosphor::software::image;
 
 void Activation::flashWrite()
 {
+#ifdef BMC_STATIC_DUAL_IMAGE
+    if (parent.runningImageSlot != 0)
+    {
+        // It's running on the secondary chip, update the primary one
+        info("Flashing primary flash from secondary, id: {ID}", "ID",
+             versionId);
+        auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
+                                          SYSTEMD_INTERFACE, "StartUnit");
+        auto serviceFile = FLASH_ALT_SERVICE_TMPL + versionId + ".service";
+        method.append(serviceFile, "replace");
+        bus.call_noreply(method);
+        return;
+    }
+#endif
     // For static layout code update, just put images in /run/initramfs.
     // It expects user to trigger a reboot and an updater script will program
     // the image to flash during reboot.
@@ -38,9 +57,33 @@
     }
 }
 
-void Activation::onStateChanges(sdbusplus::message::message& /*msg*/)
+void Activation::onStateChanges(
+    [[maybe_unused]] sdbusplus::message::message& msg)
 {
-    // Empty
+#ifdef BMC_STATIC_DUAL_IMAGE
+    uint32_t newStateID;
+    auto serviceFile = FLASH_ALT_SERVICE_TMPL + versionId + ".service";
+    sdbusplus::message::object_path newStateObjPath;
+    std::string newStateUnit{};
+    std::string newStateResult{};
+    msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
+
+    if (newStateUnit != serviceFile)
+    {
+        return;
+    }
+    if (newStateResult == "done")
+    {
+        activationProgress->progress(90);
+        onFlashWriteSuccess();
+    }
+    else
+    {
+        namespace softwareServer =
+            sdbusplus::xyz::openbmc_project::Software::server;
+        Activation::activation(softwareServer::Activation::Activations::Failed);
+    }
+#endif
 }
 
 } // namespace updater
diff --git a/static/obmc-flash-bmc-alt@.service.in b/static/obmc-flash-bmc-alt@.service.in
new file mode 100644
index 0000000..8ea4790
--- /dev/null
+++ b/static/obmc-flash-bmc-alt@.service.in
@@ -0,0 +1,7 @@
+[Unit]
+Description=Flash image-bmc to the alt chip
+
+[Service]
+Type=oneshot
+RemainAfterExit=no
+ExecStart=/usr/sbin/flashcp /tmp/images/%i/image-bmc /dev/mtd/alt-bmc