# Firmware configuration file for the Raspberry Pi
#
# IMPORTANT:
# Edit `fwup.conf.eex` and run `mix generate_fwup_conf` to generate fwup.conf.
#
require-fwup-version="1.0.0"

include("${NERVES_SDK_IMAGES:-.}/fwup_include/fwup-common.conf")

# Special case files
# * bootcode.bin - required to boot on pre-Raspberry Pi 4 devices
# * cmdline*.txt - selectively programmed based on the slot
file-resource bootcode.bin { host-path = "${NERVES_SYSTEM}/images/rpi-firmware/bootcode.bin" }
file-resource cmdline-a.txt { host-path = "${NERVES_SYSTEM}/images/cmdline-a.txt" }
file-resource cmdline-b.txt { host-path = "${NERVES_SYSTEM}/images/cmdline-b.txt" }


# File resources are listed in the order that they are included in the .fw file
# This is important, since this is the order that they're written on a firmware
# update due to the event driven nature of the update system.
file-resource start.elf { host-path = "${NERVES_SYSTEM}/images/rpi-firmware/start_x.elf" }
file-resource fixup.dat { host-path = "${NERVES_SYSTEM}/images/rpi-firmware/fixup_x.dat" }
file-resource config.txt { host-path = "${NERVES_SYSTEM}/images/config.txt" }
file-resource zImage { host-path = "${NERVES_SYSTEM}/images/zImage" }
file-resource bcm2708-rpi-b.dtb { host-path = "${NERVES_SYSTEM}/images/bcm2708-rpi-b.dtb" }
file-resource bcm2708-rpi-b-plus.dtb { host-path = "${NERVES_SYSTEM}/images/bcm2708-rpi-b-plus.dtb" }
file-resource bcm2708-rpi-cm.dtb { host-path = "${NERVES_SYSTEM}/images/bcm2708-rpi-cm.dtb" }
file-resource bcm2708-rpi-zero.dtb { host-path = "${NERVES_SYSTEM}/images/bcm2708-rpi-zero.dtb" }
file-resource bcm2708-rpi-zero-w.dtb { host-path = "${NERVES_SYSTEM}/images/bcm2708-rpi-zero-w.dtb" }
file-resource overlays/i2c-mux.dtbo { host-path = "${NERVES_SYSTEM}/images/rpi-firmware/overlays/i2c-mux.dtbo" }
file-resource overlays/imx219.dtbo { host-path = "${NERVES_SYSTEM}/images/rpi-firmware/overlays/imx219.dtbo" }
file-resource overlays/imx296.dtbo { host-path = "${NERVES_SYSTEM}/images/rpi-firmware/overlays/imx296.dtbo" }
file-resource overlays/imx477.dtbo { host-path = "${NERVES_SYSTEM}/images/rpi-firmware/overlays/imx477.dtbo" }
file-resource overlays/imx708.dtbo { host-path = "${NERVES_SYSTEM}/images/rpi-firmware/overlays/imx708.dtbo" }
file-resource overlays/miniuart-bt.dtbo { host-path = "${NERVES_SYSTEM}/images/rpi-firmware/overlays/miniuart-bt.dtbo" }
file-resource overlays/ov5647.dtbo { host-path = "${NERVES_SYSTEM}/images/rpi-firmware/overlays/ov5647.dtbo" }
file-resource overlays/ramoops.dtbo { host-path = "${NERVES_SYSTEM}/images/ramoops-overlay.dtb" }
file-resource overlays/tc358743.dtbo { host-path = "${NERVES_SYSTEM}/images/rpi-firmware/overlays/tc358743.dtbo" }
file-resource overlays/w1-gpio-pullup.dtbo { host-path = "${NERVES_SYSTEM}/images/rpi-firmware/overlays/w1-gpio-pullup.dtbo" }

file-resource rootfs.img {
    host-path = ${ROOTFS}

    # Error out if the rootfs size exceeds the partition size
    assert-size-lte = ${ROOTFS_A_PART_COUNT}
}

# This firmware task writes everything to the destination media
task complete {
    # Only match if not mounted
    require-unmounted-destination = true

    on-init {
        mbr_write(mbr)

        fat_mkfs(${AUTOBOOT_PART_OFFSET}, ${AUTOBOOT_PART_COUNT})
        fat_setlabel(${AUTOBOOT_PART_OFFSET}, "AUTOBOOT")
        fat_touch(${AUTOBOOT_PART_OFFSET}, "config.txt")

        fat_mkfs(${BOOT_A_PART_OFFSET}, ${BOOT_A_PART_COUNT})
        fat_setlabel(${BOOT_A_PART_OFFSET}, "BOOT-A")
        fat_mkdir(${BOOT_A_PART_OFFSET}, "overlays")

        uboot_clearenv(uboot-env)

        include("${NERVES_PROVISIONING}")

        uboot_setenv(uboot-env, "nerves_fw_devpath", ${NERVES_FW_DEVPATH})
        uboot_setenv(uboot-env, "a.nerves_fw_validated", "1")
        uboot_setenv(uboot-env, "a.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
        uboot_setenv(uboot-env, "a.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
        uboot_setenv(uboot-env, "a.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
        uboot_setenv(uboot-env, "a.nerves_fw_product", ${NERVES_FW_PRODUCT})
        uboot_setenv(uboot-env, "a.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
        uboot_setenv(uboot-env, "a.nerves_fw_version", ${NERVES_FW_VERSION})
        uboot_setenv(uboot-env, "a.nerves_fw_platform", ${NERVES_FW_PLATFORM})
        uboot_setenv(uboot-env, "a.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
        uboot_setenv(uboot-env, "a.nerves_fw_author", ${NERVES_FW_AUTHOR})
        uboot_setenv(uboot-env, "a.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
        uboot_setenv(uboot-env, "a.nerves_fw_misc", ${NERVES_FW_MISC})
        uboot_setenv(uboot-env, "a.nerves_fw_uuid", "\${FWUP_META_UUID}")
    }

    on-resource autoboot-a.txt { fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt") }
    on-resource bootcode.bin { fat_write(${AUTOBOOT_PART_OFFSET}, "bootcode.bin") }
    on-resource cmdline-a.txt { fat_write(${BOOT_A_PART_OFFSET}, "cmdline.txt") }

    on-resource start.elf { fat_write(${BOOT_A_PART_OFFSET}, "start.elf") }
    on-resource fixup.dat { fat_write(${BOOT_A_PART_OFFSET}, "fixup.dat") }
    on-resource config.txt { fat_write(${BOOT_A_PART_OFFSET}, "config.txt") }
    on-resource zImage { fat_write(${BOOT_A_PART_OFFSET}, "zImage") }
    on-resource bcm2708-rpi-b.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-b.dtb") }
    on-resource bcm2708-rpi-b-plus.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-b-plus.dtb") }
    on-resource bcm2708-rpi-cm.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-cm.dtb") }
    on-resource bcm2708-rpi-zero.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-zero.dtb") }
    on-resource bcm2708-rpi-zero-w.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-zero-w.dtb") }
    on-resource overlays/i2c-mux.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/i2c-mux.dtbo") }
    on-resource overlays/imx219.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/imx219.dtbo") }
    on-resource overlays/imx296.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/imx296.dtbo") }
    on-resource overlays/imx477.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/imx477.dtbo") }
    on-resource overlays/imx708.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/imx708.dtbo") }
    on-resource overlays/miniuart-bt.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/miniuart-bt.dtbo") }
    on-resource overlays/ov5647.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/ov5647.dtbo") }
    on-resource overlays/ramoops.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/ramoops.dtbo") }
    on-resource overlays/tc358743.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/tc358743.dtbo") }
    on-resource overlays/w1-gpio-pullup.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/w1-gpio-pullup.dtbo") }

    on-resource rootfs.img {
        # write to the first rootfs partition
        raw_write(${ROOTFS_A_PART_OFFSET})
    }

    on-finish {
        # Clear out any old data in the B partition that might be mistaken for
        # a file system. This is mostly to avoid confusion in humans when
        # reprogramming SDCards with unknown contents.
        raw_memset(${BOOT_B_PART_OFFSET}, 256, 0xff)
        raw_memset(${ROOTFS_B_PART_OFFSET}, 256, 0xff)

        # Invalidate the application data partition so that it is guaranteed to
        # trigger the corrupt filesystem detection code on first boot and get
        # formatted. If this isn't done and an old SDCard is reused, the
        # application data could be in a weird state.
        raw_memset(${APP_PART_OFFSET}, 256, 0xff)
    }
}

task upgrade.old-a {
    # This task upgrades Raspberry Pi's using the old (MBR-swap) partitioning scheme
    # to the new way that uses tryboot.
    require-partition-offset(1, ${OLD_ROOTFS_B_PART_OFFSET})

    # Verify the expected platform/architecture
    require-uboot-variable(uboot-env, "b.nerves_fw_platform", "${NERVES_FW_PLATFORM}")

    on-init {
        info("Migrating boot partition A to the new tryboot (automatic rollback) layout. Do not power off or interrupt this update.")

        # Clear some firmware information just in case this update gets
        # interrupted midway. If this partition was bootable, it's not going to
        # be soon.
        uboot_unsetenv(uboot-env, "a.nerves_fw_version")
        uboot_unsetenv(uboot-env, "a.nerves_fw_platform")
        uboot_unsetenv(uboot-env, "a.nerves_fw_architecture")
        uboot_unsetenv(uboot-env, "a.nerves_fw_uuid")

        # Reset the previous contents of the A boot partition
        fat_mkfs(${AUTOBOOT_PART_OFFSET}, ${AUTOBOOT_PART_COUNT})
        fat_setlabel(${AUTOBOOT_PART_OFFSET}, "AUTOBOOT")
        fat_touch(${AUTOBOOT_PART_OFFSET}, "config.txt")
        fat_mkfs(${BOOT_A_PART_OFFSET}, ${BOOT_A_PART_COUNT})
        fat_setlabel(${BOOT_A_PART_OFFSET}, "BOOT-A")
        fat_mkdir(${BOOT_A_PART_OFFSET}, "overlays")
    }

    # Write the new boot partition files and rootfs. The MBR still points
    # to the B partition, so an error or power failure has a tiny chance
    # of working.
    on-resource autoboot-a.txt { fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt") }
    on-resource bootcode.bin { fat_write(${AUTOBOOT_PART_OFFSET}, "bootcode.bin") }
    on-resource cmdline-a.txt { fat_write(${BOOT_A_PART_OFFSET}, "cmdline.txt") }

    on-resource start.elf { fat_write(${BOOT_A_PART_OFFSET}, "start.elf") }
    on-resource fixup.dat { fat_write(${BOOT_A_PART_OFFSET}, "fixup.dat") }
    on-resource config.txt { fat_write(${BOOT_A_PART_OFFSET}, "config.txt") }
    on-resource zImage { fat_write(${BOOT_A_PART_OFFSET}, "zImage") }
    on-resource bcm2708-rpi-b.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-b.dtb") }
    on-resource bcm2708-rpi-b-plus.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-b-plus.dtb") }
    on-resource bcm2708-rpi-cm.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-cm.dtb") }
    on-resource bcm2708-rpi-zero.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-zero.dtb") }
    on-resource bcm2708-rpi-zero-w.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-zero-w.dtb") }
    on-resource overlays/i2c-mux.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/i2c-mux.dtbo") }
    on-resource overlays/imx219.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/imx219.dtbo") }
    on-resource overlays/imx296.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/imx296.dtbo") }
    on-resource overlays/imx477.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/imx477.dtbo") }
    on-resource overlays/imx708.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/imx708.dtbo") }
    on-resource overlays/miniuart-bt.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/miniuart-bt.dtbo") }
    on-resource overlays/ov5647.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/ov5647.dtbo") }
    on-resource overlays/ramoops.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/ramoops.dtbo") }
    on-resource overlays/tc358743.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/tc358743.dtbo") }
    on-resource overlays/w1-gpio-pullup.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/w1-gpio-pullup.dtbo") }

    on-resource rootfs.img {
        raw_write(${ROOTFS_A_PART_OFFSET})
    }

    on-finish {
        # Update firmware metadata
        uboot_setenv(uboot-env, "a.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
        uboot_setenv(uboot-env, "a.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
        uboot_setenv(uboot-env, "a.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
        uboot_setenv(uboot-env, "a.nerves_fw_product", ${NERVES_FW_PRODUCT})
        uboot_setenv(uboot-env, "a.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
        uboot_setenv(uboot-env, "a.nerves_fw_version", ${NERVES_FW_VERSION})
        uboot_setenv(uboot-env, "a.nerves_fw_platform", ${NERVES_FW_PLATFORM})
        uboot_setenv(uboot-env, "a.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
        uboot_setenv(uboot-env, "a.nerves_fw_author", ${NERVES_FW_AUTHOR})
        uboot_setenv(uboot-env, "a.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
        uboot_setenv(uboot-env, "a.nerves_fw_misc", ${NERVES_FW_MISC})
        uboot_setenv(uboot-env, "a.nerves_fw_uuid", "\${FWUP_META_UUID}")
        uboot_unsetenv(uboot-env, "nerves_fw_active") # No longer used
        uboot_unsetenv(uboot-env, "nerves_fw_validated") # No longer used

        # Validate since can't go back
        uboot_setenv(uboot-env, "a.nerves_fw_validated", "1")
        uboot_setenv(uboot-env, "b.nerves_fw_validated", "0")
        mbr_write(mbr)
    }
}

task upgrade.old-b {
    # This task upgrades Raspberry Pi's using the old (MBR-swap) partitioning scheme
    # to the new way that uses tryboot.
    require-partition-offset(1, ${OLD_ROOTFS_A_PART_OFFSET})

    # Verify the expected platform/architecture
    require-uboot-variable(uboot-env, "a.nerves_fw_platform", "${NERVES_FW_PLATFORM}")

    on-init {
        info("Migrating boot partition B to the new tryboot (automatic rollback) layout. Do not power off or interrupt this update.")

        # Clear some firmware information just in case this update gets
        # interrupted midway. If this partition was bootable, it's not going to
        # be soon.
        uboot_unsetenv(uboot-env, "b.nerves_fw_version")
        uboot_unsetenv(uboot-env, "b.nerves_fw_platform")
        uboot_unsetenv(uboot-env, "b.nerves_fw_architecture")
        uboot_unsetenv(uboot-env, "b.nerves_fw_uuid")

        # Reset the previous contents of the B boot partition
        fat_mkfs(${AUTOBOOT_PART_OFFSET}, ${AUTOBOOT_PART_COUNT})
        fat_setlabel(${AUTOBOOT_PART_OFFSET}, "AUTOBOOT")
        fat_mkfs(${BOOT_B_PART_OFFSET}, ${BOOT_B_PART_COUNT})
        fat_touch(${AUTOBOOT_PART_OFFSET}, "config.txt")
        fat_setlabel(${BOOT_B_PART_OFFSET}, "BOOT-B")
        fat_mkdir(${BOOT_B_PART_OFFSET}, "overlays")
    }

    # Write the new boot partition files and rootfs. The MBR still points
    # to the B partition, so an error or power failure has a tiny chance
    # of working.
    on-resource autoboot-b.txt { fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt") }
    on-resource bootcode.bin { fat_write(${AUTOBOOT_PART_OFFSET}, "bootcode.bin") }
    on-resource cmdline-b.txt { fat_write(${BOOT_B_PART_OFFSET}, "cmdline.txt") }

    on-resource start.elf { fat_write(${BOOT_B_PART_OFFSET}, "start.elf") }
    on-resource fixup.dat { fat_write(${BOOT_B_PART_OFFSET}, "fixup.dat") }
    on-resource config.txt { fat_write(${BOOT_B_PART_OFFSET}, "config.txt") }
    on-resource zImage { fat_write(${BOOT_B_PART_OFFSET}, "zImage") }
    on-resource bcm2708-rpi-b.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2708-rpi-b.dtb") }
    on-resource bcm2708-rpi-b-plus.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2708-rpi-b-plus.dtb") }
    on-resource bcm2708-rpi-cm.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2708-rpi-cm.dtb") }
    on-resource bcm2708-rpi-zero.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2708-rpi-zero.dtb") }
    on-resource bcm2708-rpi-zero-w.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2708-rpi-zero-w.dtb") }
    on-resource overlays/i2c-mux.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/i2c-mux.dtbo") }
    on-resource overlays/imx219.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/imx219.dtbo") }
    on-resource overlays/imx296.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/imx296.dtbo") }
    on-resource overlays/imx477.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/imx477.dtbo") }
    on-resource overlays/imx708.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/imx708.dtbo") }
    on-resource overlays/miniuart-bt.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/miniuart-bt.dtbo") }
    on-resource overlays/ov5647.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/ov5647.dtbo") }
    on-resource overlays/ramoops.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/ramoops.dtbo") }
    on-resource overlays/tc358743.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/tc358743.dtbo") }
    on-resource overlays/w1-gpio-pullup.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/w1-gpio-pullup.dtbo") }

    on-resource rootfs.img {
        raw_write(${ROOTFS_B_PART_OFFSET})
    }

    on-finish {
        # Update firmware metadata
        uboot_setenv(uboot-env, "b.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
        uboot_setenv(uboot-env, "b.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
        uboot_setenv(uboot-env, "b.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
        uboot_setenv(uboot-env, "b.nerves_fw_product", ${NERVES_FW_PRODUCT})
        uboot_setenv(uboot-env, "b.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
        uboot_setenv(uboot-env, "b.nerves_fw_version", ${NERVES_FW_VERSION})
        uboot_setenv(uboot-env, "b.nerves_fw_platform", ${NERVES_FW_PLATFORM})
        uboot_setenv(uboot-env, "b.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
        uboot_setenv(uboot-env, "b.nerves_fw_author", ${NERVES_FW_AUTHOR})
        uboot_setenv(uboot-env, "b.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
        uboot_setenv(uboot-env, "b.nerves_fw_misc", ${NERVES_FW_MISC})
        uboot_setenv(uboot-env, "b.nerves_fw_uuid", "\${FWUP_META_UUID}")
        uboot_unsetenv(uboot-env, "nerves_fw_active") # No longer used
        uboot_unsetenv(uboot-env, "nerves_fw_validated") # No longer used

        # Validate since can't go back
        uboot_setenv(uboot-env, "b.nerves_fw_validated", "1")
        uboot_setenv(uboot-env, "a.nerves_fw_validated", "0")
        mbr_write(mbr)
    }
}

task upgrade.a {
    # This task upgrades the A partition

    # Require that the running version of firmware has been validated.
    # If it has not, then failing back is not guaranteed to work.
    require-path-at-offset("/", ${ROOTFS_B_PART_OFFSET})
    require-uboot-variable(uboot-env, "b.nerves_fw_validated", "1")

    # Verify the expected platform/architecture
    require-uboot-variable(uboot-env, "b.nerves_fw_platform", "${NERVES_FW_PLATFORM}")
    require-uboot-variable(uboot-env, "b.nerves_fw_architecture", "${NERVES_FW_ARCHITECTURE}")

    on-init {
        info("Upgrading partition A")

        # Clear some firmware information just in case this update gets
        # interrupted midway. If this partition was bootable, it's not going to
        # be soon.
        uboot_setenv(uboot-env, "a.nerves_fw_validated", "0")
        uboot_unsetenv(uboot-env, "a.nerves_fw_version")
        uboot_unsetenv(uboot-env, "a.nerves_fw_platform")
        uboot_unsetenv(uboot-env, "a.nerves_fw_architecture")
        uboot_unsetenv(uboot-env, "a.nerves_fw_uuid")

        # Reset the previous contents of the A boot partition
        fat_mkfs(${BOOT_A_PART_OFFSET}, ${BOOT_A_PART_COUNT})
        fat_setlabel(${BOOT_A_PART_OFFSET}, "BOOT-A")
        fat_mkdir(${BOOT_A_PART_OFFSET}, "overlays")

        # Indicate that the entire partition can be cleared
        trim(${ROOTFS_A_PART_OFFSET}, ${ROOTFS_A_PART_COUNT})
    }

    # Write the new boot partition files and rootfs. The MBR still points
    # to the B partition, so an error or power failure during this part
    # won't hurt anything.
    on-resource cmdline-a.txt { fat_write(${BOOT_A_PART_OFFSET}, "cmdline.txt") }
    on-resource start.elf { fat_write(${BOOT_A_PART_OFFSET}, "start.elf") }
    on-resource fixup.dat { fat_write(${BOOT_A_PART_OFFSET}, "fixup.dat") }
    on-resource config.txt { fat_write(${BOOT_A_PART_OFFSET}, "config.txt") }
    on-resource zImage { fat_write(${BOOT_A_PART_OFFSET}, "zImage") }
    on-resource bcm2708-rpi-b.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-b.dtb") }
    on-resource bcm2708-rpi-b-plus.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-b-plus.dtb") }
    on-resource bcm2708-rpi-cm.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-cm.dtb") }
    on-resource bcm2708-rpi-zero.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-zero.dtb") }
    on-resource bcm2708-rpi-zero-w.dtb { fat_write(${BOOT_A_PART_OFFSET}, "bcm2708-rpi-zero-w.dtb") }
    on-resource overlays/i2c-mux.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/i2c-mux.dtbo") }
    on-resource overlays/imx219.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/imx219.dtbo") }
    on-resource overlays/imx296.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/imx296.dtbo") }
    on-resource overlays/imx477.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/imx477.dtbo") }
    on-resource overlays/imx708.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/imx708.dtbo") }
    on-resource overlays/miniuart-bt.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/miniuart-bt.dtbo") }
    on-resource overlays/ov5647.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/ov5647.dtbo") }
    on-resource overlays/ramoops.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/ramoops.dtbo") }
    on-resource overlays/tc358743.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/tc358743.dtbo") }
    on-resource overlays/w1-gpio-pullup.dtbo { fat_write(${BOOT_A_PART_OFFSET}, "overlays/w1-gpio-pullup.dtbo") }

    on-resource rootfs.img {
        delta-source-raw-offset=${ROOTFS_B_PART_OFFSET}
        delta-source-raw-count=${ROOTFS_B_PART_COUNT}
        raw_write(${ROOTFS_A_PART_OFFSET})
    }

    on-finish {
        # Update firmware metadata
        uboot_setenv(uboot-env, "a.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
        uboot_setenv(uboot-env, "a.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
        uboot_setenv(uboot-env, "a.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
        uboot_setenv(uboot-env, "a.nerves_fw_product", ${NERVES_FW_PRODUCT})
        uboot_setenv(uboot-env, "a.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
        uboot_setenv(uboot-env, "a.nerves_fw_version", ${NERVES_FW_VERSION})
        uboot_setenv(uboot-env, "a.nerves_fw_platform", ${NERVES_FW_PLATFORM})
        uboot_setenv(uboot-env, "a.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
        uboot_setenv(uboot-env, "a.nerves_fw_author", ${NERVES_FW_AUTHOR})
        uboot_setenv(uboot-env, "a.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
        uboot_setenv(uboot-env, "a.nerves_fw_misc", ${NERVES_FW_MISC})
        uboot_setenv(uboot-env, "a.nerves_fw_uuid", "\${FWUP_META_UUID}")

	# Try partition A on next boot
        reboot_param("0 tryboot")
    }

    on-error {
    }
}

task upgrade.b {
    # This task upgrades the B partition

    # Require that the running version of firmware has been validated.
    # If it has not, then failing back is not guaranteed to work.
    require-path-at-offset("/", ${ROOTFS_A_PART_OFFSET})
    require-uboot-variable(uboot-env, "a.nerves_fw_validated", "1")

    # Verify the expected platform/architecture
    require-uboot-variable(uboot-env, "a.nerves_fw_platform", "${NERVES_FW_PLATFORM}")
    require-uboot-variable(uboot-env, "a.nerves_fw_architecture", "${NERVES_FW_ARCHITECTURE}")

    on-init {
        info("Upgrading partition B")

        # Clear some firmware information just in case this update gets
        # interrupted midway.
        uboot_setenv(uboot-env, "b.nerves_fw_validated", "0")
        uboot_unsetenv(uboot-env, "b.nerves_fw_version")
        uboot_unsetenv(uboot-env, "b.nerves_fw_platform")
        uboot_unsetenv(uboot-env, "b.nerves_fw_architecture")
        uboot_unsetenv(uboot-env, "b.nerves_fw_uuid")

        # Reset the previous contents of the B boot partition
        fat_mkfs(${BOOT_B_PART_OFFSET}, ${BOOT_B_PART_COUNT})
        fat_setlabel(${BOOT_B_PART_OFFSET}, "BOOT-B")
        fat_mkdir(${BOOT_B_PART_OFFSET}, "overlays")

        trim(${ROOTFS_B_PART_OFFSET}, ${ROOTFS_B_PART_COUNT})
    }

    # Write the new boot partition files and rootfs. The MBR still points
    # to the A partition, so an error or power failure during this part
    # won't hurt anything.
    on-resource cmdline-b.txt { fat_write(${BOOT_B_PART_OFFSET}, "cmdline.txt") }
    on-resource start.elf { fat_write(${BOOT_B_PART_OFFSET}, "start.elf") }
    on-resource fixup.dat { fat_write(${BOOT_B_PART_OFFSET}, "fixup.dat") }
    on-resource config.txt { fat_write(${BOOT_B_PART_OFFSET}, "config.txt") }
    on-resource zImage { fat_write(${BOOT_B_PART_OFFSET}, "zImage") }
    on-resource bcm2708-rpi-b.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2708-rpi-b.dtb") }
    on-resource bcm2708-rpi-b-plus.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2708-rpi-b-plus.dtb") }
    on-resource bcm2708-rpi-cm.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2708-rpi-cm.dtb") }
    on-resource bcm2708-rpi-zero.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2708-rpi-zero.dtb") }
    on-resource bcm2708-rpi-zero-w.dtb { fat_write(${BOOT_B_PART_OFFSET}, "bcm2708-rpi-zero-w.dtb") }
    on-resource overlays/i2c-mux.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/i2c-mux.dtbo") }
    on-resource overlays/imx219.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/imx219.dtbo") }
    on-resource overlays/imx296.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/imx296.dtbo") }
    on-resource overlays/imx477.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/imx477.dtbo") }
    on-resource overlays/imx708.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/imx708.dtbo") }
    on-resource overlays/miniuart-bt.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/miniuart-bt.dtbo") }
    on-resource overlays/ov5647.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/ov5647.dtbo") }
    on-resource overlays/ramoops.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/ramoops.dtbo") }
    on-resource overlays/tc358743.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/tc358743.dtbo") }
    on-resource overlays/w1-gpio-pullup.dtbo { fat_write(${BOOT_B_PART_OFFSET}, "overlays/w1-gpio-pullup.dtbo") }

    on-resource rootfs.img {
        delta-source-raw-offset=${ROOTFS_A_PART_OFFSET}
        delta-source-raw-count=${ROOTFS_A_PART_COUNT}
        raw_write(${ROOTFS_B_PART_OFFSET})
    }

    on-finish {
        # Update firmware metadata
        uboot_setenv(uboot-env, "b.nerves_fw_application_part0_devpath", ${NERVES_FW_APPLICATION_PART0_DEVPATH})
        uboot_setenv(uboot-env, "b.nerves_fw_application_part0_fstype", ${NERVES_FW_APPLICATION_PART0_FSTYPE})
        uboot_setenv(uboot-env, "b.nerves_fw_application_part0_target", ${NERVES_FW_APPLICATION_PART0_TARGET})
        uboot_setenv(uboot-env, "b.nerves_fw_product", ${NERVES_FW_PRODUCT})
        uboot_setenv(uboot-env, "b.nerves_fw_description", ${NERVES_FW_DESCRIPTION})
        uboot_setenv(uboot-env, "b.nerves_fw_version", ${NERVES_FW_VERSION})
        uboot_setenv(uboot-env, "b.nerves_fw_platform", ${NERVES_FW_PLATFORM})
        uboot_setenv(uboot-env, "b.nerves_fw_architecture", ${NERVES_FW_ARCHITECTURE})
        uboot_setenv(uboot-env, "b.nerves_fw_author", ${NERVES_FW_AUTHOR})
        uboot_setenv(uboot-env, "b.nerves_fw_vcs_identifier", ${NERVES_FW_VCS_IDENTIFIER})
        uboot_setenv(uboot-env, "b.nerves_fw_misc", ${NERVES_FW_MISC})
        uboot_setenv(uboot-env, "b.nerves_fw_uuid", "\${FWUP_META_UUID}")

	# Try partition B on next boot
        reboot_param("0 tryboot")
    }

    on-error {
    }
}

task upgrade.unexpected {
    require-uboot-variable(uboot-env, "a.nerves_fw_platform", "${NERVES_FW_PLATFORM}")
    require-uboot-variable(uboot-env, "a.nerves_fw_architecture", "${NERVES_FW_ARCHITECTURE}")
    on-init {
        error("Please check the media being upgraded. It doesn't look like either the A or B partitions are active.")
    }
}

task upgrade.wrongplatform {
    on-init {
        error("Expecting platform=${NERVES_FW_PLATFORM} and architecture=${NERVES_FW_ARCHITECTURE}")
    }
}

task provision {
    require-uboot-variable(uboot-env, "a.nerves_fw_platform", "${NERVES_FW_PLATFORM}")
    require-uboot-variable(uboot-env, "a.nerves_fw_architecture", "${NERVES_FW_ARCHITECTURE}")
    on-init {
        include("${NERVES_PROVISIONING}")
    }
}
task provision.wrongplatform {
    on-init {
        error("Expecting platform=${NERVES_FW_PLATFORM} and architecture=${NERVES_FW_ARCHITECTURE}")
    }
}
