require-fwup-version="1.4.0"  # For the GPT support

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

file-resource fsbl1 {
    host-path = "${NERVES_SYSTEM}/images/tf-a-stm32mp135f-coremp135.stm32"
    # Error out if the uboot spl binary size exceeds the partition size
    assert-size-lte = ${SPL1_PART_COUNT}
}

file-resource fsbl2 {
    host-path = "${NERVES_SYSTEM}/images/tf-a-stm32mp135f-coremp135.stm32"
    # Error out if the uboot spl binary size exceeds the partition size
    assert-size-lte = ${SPL1_PART_COUNT}
}

file-resource fip.bin {
    host-path = "${NERVES_SYSTEM}/images/fip.bin"
    # Error out if the uboot binary size exceeds the partition size
    assert-size-lte = ${UBOOT_PART_COUNT}
}

file-resource stm32mp135f-coremp135.dtb {
    host-path = "${NERVES_SYSTEM}/images/stm32mp135f-coremp135.dtb"
}

file-resource zImage {
    host-path = "${NERVES_SYSTEM}/images/zImage"
}

file-resource extlinux.conf {
    host-path = "${NERVES_SYSTEM}/images/extlinux.conf"
}

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

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

gpt gpt-a {
    guid = ${NERVES_SYSTEM_DISK_UUID}
    partition 0 {
        block-offset = ${SPL1_PART_OFFSET}
        block-count = ${SPL1_PART_COUNT}
        guid = ${NERVES_SYSTEM_SPL_PART_UUID}
        type = ${LINUX_FILESYSTEM_DATA_TYPE}
        name = "fsbl1"
    }
    partition 1 {
        block-offset = ${SPL2_PART_OFFSET}
        block-count = ${SPL2_PART_COUNT}
        guid = ${NERVES_SYSTEM_SPL_PART_UUID}
        type = ${LINUX_FILESYSTEM_DATA_TYPE}
        name = "fsbl2"
    }
    partition 2 {
        block-offset = ${UBOOT_PART_OFFSET}
        block-count = ${UBOOT_PART_COUNT}
        guid = ${NERVES_SYSETM_UBOOT_PART_UUID}
        type = ${LINUX_FILESYSTEM_DATA_TYPE}
        name = "fip"
    }
    partition 3 {
        block-offset = ${EFI_A_PART_OFFSET}
        block-count = ${EFI_A_PART_COUNT}
        guid = ${NERVES_SYSTEM_EFI_PART_UUID}
        type = ${EFI_TYPE}
        name = "boot"
    }
    partition 4 {
        block-offset = ${ROOTFS_A_PART_OFFSET}
        block-count = ${ROOTFS_A_PART_COUNT}
        guid = ${NERVES_SYSTEM_ROOTFS_PART_UUID}
        name = "rootfs"
        type = ${LINUX_FILESYSTEM_DATA_TYPE}
        flags = 0x4
    }
    partition 5 {
        block-offset = ${APP_PART_OFFSET}
        block-count = ${APP_PART_COUNT}
        guid = ${NERVES_SYSTEM_APP_PART_UUID}
        type = ${LINUX_FILESYSTEM_DATA_TYPE}
        expand = true
    }
}

gpt gpt-b {
    guid = ${NERVES_SYSTEM_DISK_UUID}
    partition 0 {
        block-offset = ${SPL1_PART_OFFSET}
        block-count = ${SPL1_PART_COUNT}
        guid = ${NERVES_SYSTEM_SPL_PART_UUID}
        type = ${LINUX_FILESYSTEM_DATA_TYPE}
        name = "fsbl1"
    }
    partition 1 {
        block-offset = ${SPL2_PART_OFFSET}
        block-count = ${SPL2_PART_COUNT}
        guid = ${NERVES_SYSTEM_SPL_PART_UUID}
        type = ${LINUX_FILESYSTEM_DATA_TYPE}
        name = "fsbl2"
    }
    partition 2 {
        block-offset = ${UBOOT_PART_OFFSET}
        block-count = ${UBOOT_PART_COUNT}
        guid = ${NERVES_SYSETM_UBOOT_PART_UUID}
        type = ${LINUX_FILESYSTEM_DATA_TYPE}
        name = "fip"
    }
    partition 3 {
        block-offset = ${EFI_B_PART_OFFSET}
        block-count = ${EFI_B_PART_COUNT}
        guid = ${NERVES_SYSTEM_EFI_PART_UUID}
        type = ${EFI_TYPE}
        name = "boot"
    }
    partition 4 {
        block-offset = ${ROOTFS_B_PART_OFFSET}
        block-count = ${ROOTFS_B_PART_COUNT}
        guid = ${NERVES_SYSTEM_ROOTFS_PART_UUID}
        name = "rootfs"
        type = ${LINUX_FILESYSTEM_DATA_TYPE}
        flags = 0x4
    }
    partition 5 {
        block-offset = ${APP_PART_OFFSET}
        block-count = ${APP_PART_COUNT}
        guid = ${NERVES_SYSTEM_APP_PART_UUID}
        type = ${LINUX_FILESYSTEM_DATA_TYPE}
        expand = true
    }
}

# Location where installed firmware information is stored.
# While this is called "u-boot", u-boot isn't involved in this
# setup. It just provides a convenient key/value store format.
uboot-environment uboot-env {
    block-offset = ${UBOOT_ENV_OFFSET}
    block-count = ${UBOOT_ENV_COUNT}
}

# This firmware task writes everything to the destination media.
# This should only be run at the factory to initialize a board!
task complete {
    # Only match if not mounted
    require-unmounted-destination = true

    on-init {
        gpt_write(gpt-a)

        uboot_clearenv(uboot-env)

        include("${NERVES_PROVISIONING}")

        uboot_setenv(uboot-env, "nerves_fw_active", "a")
        uboot_setenv(uboot-env, "nerves_fw_autovalidate", "0")
        uboot_setenv(uboot-env, "nerves_fw_validated", "1")
        uboot_setenv(uboot-env, "nerves_fw_booted", "0")
        uboot_setenv(uboot-env, "nerves_fw_devpath", ${NERVES_FW_DEVPATH})
        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}")

        fat_mkfs(${EFI_A_PART_OFFSET}, ${EFI_A_PART_COUNT})
        fat_setlabel(${EFI_A_PART_OFFSET}, "BOOT-A")
        fat_mkdir(${EFI_A_PART_OFFSET}, "/boot")
        fat_mkdir(${EFI_A_PART_OFFSET}, "/boot/extlinux")
    }

    on-resource fsbl1 { raw_write(${SPL1_PART_OFFSET}) }

    on-resource fsbl2 { raw_write(${SPL1_PART_OFFSET}) }

    on-resource fip.bin { raw_write(${UBOOT_PART_OFFSET}) }

    on-resource extlinux.conf { fat_write(${EFI_A_PART_OFFSET}, "/boot/extlinux/extlinux.conf") }

    on-resource zImage { fat_write(${EFI_A_PART_OFFSET}, "/boot/extlinux/zImage") }

    on-resource stm32mp135f-coremp135.dtb { fat_write(${EFI_A_PART_OFFSET}, "/boot/extlinux/stm32mp135f-coremp135.dtb") }

    on-resource rootfs.img { 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(${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.a {
    # This task upgrades the A partition, so make sure we're running
    # on B.
    require-uboot-variable(uboot-env, "nerves_fw_active", "b")

    # Require that the running version of firmware has been validated.
    # If it has not, then failing back is not guaranteed to work.
    require-uboot-variable(uboot-env, "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_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")

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

        # Reset the previous contents of the A boot partition
        fat_mkfs(${EFI_A_PART_OFFSET}, ${EFI_A_PART_COUNT})
        fat_mkdir(${EFI_A_PART_OFFSET}, "/boot")
        fat_mkdir(${EFI_A_PART_OFFSET}, "/boot/extlinux")
    }


    on-resource extlinux.conf { fat_write(${EFI_A_PART_OFFSET}, "/boot/extlinux/extlinux.conf") }

    on-resource zImage { fat_write(${EFI_A_PART_OFFSET}, "/boot/extlinux/zImage") }

    on-resource stm32mp135f-coremp135.dtb { fat_write(${EFI_A_PART_OFFSET}, "/boot/extlinux/stm32mp135f-coremp135.dtb") }

    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}")

      # Reset the validation status and boot to A
      # next time.
      uboot_setenv(uboot-env, "nerves_fw_active", "a")
      gpt_write(gpt-a)
    }
}

task upgrade.b {
    # This task upgrades the B partition, so make sure we're running
    # on A.
    require-uboot-variable(uboot-env, "nerves_fw_active", "a")

    # Require that the running version of firmware has been validated.
    # If it has not, then failing back is not guaranteed to work.
    require-uboot-variable(uboot-env, "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_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")

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

      fat_mkfs(${EFI_B_PART_OFFSET}, ${EFI_B_PART_COUNT})
      fat_mkdir(${EFI_B_PART_OFFSET}, "/boot")
      fat_mkdir(${EFI_B_PART_OFFSET}, "/boot/extlinux")
    }

    on-resource extlinux.conf { fat_write(${EFI_B_PART_OFFSET}, "/boot/extlinux/extlinux.conf") }

    on-resource zImage { fat_write(${EFI_B_PART_OFFSET}, "/boot/extlinux/zImage") }

    on-resource stm32mp135f-coremp135.dtb { fat_write(${EFI_B_PART_OFFSET}, "/boot/extlinux/stm32mp135f-coremp135.dtb") }

    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}")

      # Reset the validation status and boot to B next time.
      uboot_setenv(uboot-env, "nerves_fw_active", "b")
      gpt_write(gpt-b)
    }
}

task upgrade.unvalidated {
    require-uboot-variable(uboot-env, "nerves_fw_validated", "0")

    on-init {
        error("Please validate the running firmware before upgrading it again.")
    }
}

task upgrade.wrong {
    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}")
    }
}
