Add Powercycle Blob recipe for drainless update

This makes GBMC_POWERCYCLE_VERSION to select uboot and cpld versions
during the image build.

Tested: Build passed and blob is picked up in the image, image booted on
machine.

Google-Bug-Id: 402505598
Change-Id: I85cf90e694993ce7411c1ccca721110ff7091c25
Signed-off-by: Yuxiao Zhang <yuxiaozhang@google.com>
diff --git a/recipes-google/gbmc-powercycle-blob/gbmc-powercycle-blob.bb b/recipes-google/gbmc-powercycle-blob/gbmc-powercycle-blob.bb
new file mode 100644
index 0000000..63dd062
--- /dev/null
+++ b/recipes-google/gbmc-powercycle-blob/gbmc-powercycle-blob.bb
@@ -0,0 +1,84 @@
+SUMMARY = "GBMC Powercycle Blobs"
+DESCRIPTION = "Recipe to override U-Boot and CPLD binaries based on GBMC_POWERCYCLE_VERSION"
+LICENSE = "CLOSED"
+
+GBMC_POWERCYCLE_VERSION ??= ""
+GBMC_CONFIG ??= "dev"
+GBMC_POWERCYCLE_DEPLOY_DEPENDS ??= ""
+
+S = "${WORKDIR}"
+
+# SRC_URI will be appended by platform layers
+SRC_URI = ""
+
+python do_apply_uboot_override() {
+    import os
+    import shutil
+
+    version = d.getVar('GBMC_POWERCYCLE_VERSION')
+    if not version:
+        bb.note("GBMC_POWERCYCLE_VERSION is not set, skipping u-boot override.")
+        return
+
+    config = d.getVar('GBMC_CONFIG') or 'dev'
+    workdir = d.getVar('WORKDIR')
+    uboot_blob_path = os.path.join(workdir, version, 'u-boot', config)
+    if not os.path.exists(uboot_blob_path):
+        bb.fatal("Directory %s does not exist for GBMC_CONFIG=%s even though GBMC_POWERCYCLE_VERSION is set." % (uboot_blob_path, config))
+
+    deploy_dir_image = d.getVar('DEPLOY_DIR_IMAGE')
+    src_uboot = os.path.join(uboot_blob_path, 'u-boot.bin.merged')
+    dst_uboot = os.path.join(deploy_dir_image, 'u-boot.bin.merged')
+
+    if os.path.exists(src_uboot):
+        bb.note("Replacing U-Boot with %s" % src_uboot)
+        os.makedirs(deploy_dir_image, exist_ok=True)
+        shutil.copyfile(src_uboot, dst_uboot)
+    else:
+        bb.fatal("u-boot.bin.merged not found in %s, skipping u-boot override." % uboot_blob_path)
+}
+
+python do_apply_rootfs_override() {
+    import os
+    import shutil
+    import glob
+
+    version = d.getVar('GBMC_POWERCYCLE_VERSION')
+    if not version:
+        bb.note("GBMC_POWERCYCLE_VERSION is not set, skipping rootfs override.")
+        return
+
+    workdir = d.getVar('WORKDIR')
+    rootfs_blob_path = os.path.join(workdir, version, 'rootfs')
+
+    if not os.path.exists(rootfs_blob_path):
+        bb.note("No rootfs directory found in %s, skipping rootfs override." % os.path.join(workdir, version))
+        return
+
+    tmpdir = d.getVar('TMPDIR')
+    rootfs_pattern = os.path.join(tmpdir, 'work', '*', 'obmc-phosphor-image', '*', 'rootfs')
+    rootfs_dirs = glob.glob(rootfs_pattern)
+
+    if not rootfs_dirs:
+        bb.fatal("Could not find obmc-phosphor-image rootfs in %s" % tmpdir)
+
+    rootfs_dir = rootfs_dirs[0]
+    bb.note("Overwriting files in rootfs: %s" % rootfs_dir)
+
+    for root, dirs, files in os.walk(rootfs_blob_path):
+        for f in files:
+            src_file = os.path.join(root, f)
+            rel_path = os.path.relpath(src_file, rootfs_blob_path)
+            dst_file = os.path.join(rootfs_dir, rel_path)
+            dst_dir = os.path.dirname(dst_file)
+            os.makedirs(dst_dir, exist_ok=True)
+            bb.note("  Copying %s to %s" % (src_file, dst_file))
+            shutil.copyfile(src_file, dst_file)
+}
+
+addtask apply_rootfs_override before do_build after do_compile
+addtask apply_uboot_override before do_build after do_compile
+do_apply_uboot_override[depends] += "${GBMC_POWERCYCLE_DEPLOY_DEPENDS}"
+do_apply_rootfs_override[depends] += "obmc-phosphor-image:do_rootfs"
+
+ALLOW_EMPTY:${PN} = "1"
diff --git a/recipes-phosphor/images/obmc-phosphor-image.bbappend b/recipes-phosphor/images/obmc-phosphor-image.bbappend
index 03950ab..dca07e7 100644
--- a/recipes-phosphor/images/obmc-phosphor-image.bbappend
+++ b/recipes-phosphor/images/obmc-phosphor-image.bbappend
@@ -39,3 +39,6 @@
 OBMC_IMAGE_EXTRA_INSTALL:append:gbmc = "${@bb.utils.contains('MACHINE_FEATURES', 'gbmc-image-on-emmc', ' emmc-image-setup', '', d)}"
 
 OBMC_IMAGE_EXTRA_INSTALL:append:gbmc = " nftables-gbmc-rules"
+
+do_modify_bootloaders[depends] += " gbmc-powercycle-blob:do_apply_uboot_override"
+do_image[depends] += " gbmc-powercycle-blob:do_apply_rootfs_override"