# Firmware configuration file for the Raspberry Pi
#
# This configuration provides a SDCard/eMMC layout and firmware update
# image that supports ping/pong updates. I.e., there are two partitions
# that contain the application, but only one is in use at a time. When it
# is time to apply a firmware update, the update is written to the unused
# partition. After the write completes successfully, the system is updated
# so that the formerly unused partition is now active.

define(FINALIZE_FW, "/mnt/finalize.fw")

# Default ROOTFS if not specified
define(ROOTFS, "${PROJECT_ROOT}/output/images/rootfs.ext2")

# Image layout
# +--------------------------+
# | MBR                      |
# +--------------------------+
# | Boot partition (FAT32)   |
# | zImage, bootcode.bin,    |
# | config.txt, etc.         |
# +--------------------------+
# | Rootfs A (EXT4)          |
# +--------------------------+
# | Rootfs B (EXT4)          |
# +--------------------------+
# | Application data (FAT32) |
# +--------------------------+

# The Raspberry Pi is incredibly picky on the partition sizes
# and in ways that I don't understand. Test changes one at a
# time to make sure that they boot.
define(BOOT_PART_OFFSET, 63)
#define(BOOT_PART_COUNT, 77261)
define(BOOT_PART_COUNT, 131072)

# Let the rootfs have room to grow up to 128 MiB and align
# it to the nearest 1 MB boundary
#define(ROOTFS_A_PART_OFFSET, 77324)
#define(ROOTFS_A_PART_COUNT, 289044)
#define(ROOTFS_B_PART_OFFSET, 366368)
#define(ROOTFS_B_PART_COUNT, 289044)
define(ROOTFS_A_PART_OFFSET, 131135)
define(ROOTFS_A_PART_COUNT, 524288)
define(ROOTFS_B_PART_OFFSET, 655423)
define(ROOTFS_B_PART_COUNT, 524288)

# Application data partition. This partition can occupy all of the
# remaining space. Size it to fit the destination.
#define(APP_DATA_PART_OFFSET, 655412)
#define(APP_DATA_PART_COUNT, 1799406)
define(APP_DATA_PART_OFFSET, 1179711)
define(APP_DATA_PART_COUNT, 1799406)

# Firmware metadata
meta-product = "Ping/pong update demo image for the Raspberry Pi"
meta-description = "This image boots to Linux."
meta-version = "${FW_VERSION}"
meta-platform = "rpi"
meta-architecture = "arm"
meta-author = "Frank Hunleth"

# 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 bootcode.bin {
    host-path = "${PROJECT_ROOT}/output/images/rpi-firmware/bootcode.bin"
}
file-resource fixup.dat {
    host-path = "${PROJECT_ROOT}/output/images/rpi-firmware/fixup.dat"
}
file-resource start.elf {
    host-path = "${PROJECT_ROOT}/output/images/rpi-firmware/start.elf"
}
file-resource config.txt {
    host-path = "${PROJECT_ROOT}/output/images/rpi-firmware/config.txt"
}
file-resource zImage {
    host-path = "${PROJECT_ROOT}/output/images/zImage"
}
file-resource dt-blob.bin {
    host-path = "${PROJECT_ROOT}/board/raspberrypi/stak/dt-blob.bin"
}

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

mbr mbr-a {
    partition 0 {
        block-offset = ${BOOT_PART_OFFSET}
        block-count = ${BOOT_PART_COUNT}
        type = 0xc # FAT32
        boot = true
    }
    partition 1 {
        block-offset = ${ROOTFS_A_PART_OFFSET}
        block-count = ${ROOTFS_A_PART_COUNT}
        type = 0x83 # Linux
    }
    partition 2 {
        block-offset = ${ROOTFS_B_PART_OFFSET}
        block-count = ${ROOTFS_B_PART_COUNT}
        type = 0x83 # Linux
    }
    partition 3 {
        block-offset = ${APP_DATA_PART_OFFSET}
        block-count = ${APP_DATA_PART_COUNT}
        type = 0x0b # FAT32
    }
}

mbr mbr-b {
    partition 0 {
        block-offset = ${BOOT_PART_OFFSET}
        block-count = ${BOOT_PART_COUNT}
        type = 0xc # FAT32
        boot = true
    }
    partition 1 {
        block-offset = ${ROOTFS_B_PART_OFFSET}
        block-count = ${ROOTFS_B_PART_COUNT}
        type = 0x83 # Linux
    }
    partition 2 {
        block-offset = ${ROOTFS_A_PART_OFFSET}
        block-count = ${ROOTFS_A_PART_COUNT}
        type = 0x83 # Linux
    }
    partition 3 {
        block-offset = ${APP_DATA_PART_OFFSET}
        block-count = ${APP_DATA_PART_COUNT}
        type = 0x0b # FAT32
    }
}

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

    # Everything that gets written can be verified on the fly.
    # This speeds things up, since we don't care about detecting
    # errors before data gets written.
    verify-on-the-fly = true

    on-init {
        mbr_write(mbr-a)
        fat_mkfs(${BOOT_PART_OFFSET}, ${BOOT_PART_COUNT})
        fat_mkfs(${APP_DATA_PART_OFFSET}, ${APP_DATA_PART_COUNT})
    }

    on-resource config.txt { fat_write(${BOOT_PART_OFFSET}, "config.txt") }
    on-resource bootcode.bin { fat_write(${BOOT_PART_OFFSET}, "bootcode.bin") }
    on-resource start.elf { fat_write(${BOOT_PART_OFFSET}, "start.elf") }
    on-resource fixup.dat { fat_write(${BOOT_PART_OFFSET}, "fixup.dat") }
    on-resource zImage { fat_write(${BOOT_PART_OFFSET}, "zImage") }
    on-resource dt-blob.bin { fat_write(${BOOT_PART_OFFSET}, "dt-blob.bin") }

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

    on-finish {
    }
}

task upgrade.a {
    # This task upgrades the A partition
    require-partition1-offset = ${ROOTFS_B_PART_OFFSET}

    # Since the upgrade won't run until it has been finalized, it's ok
    # to write data as it is read.
    verify-on-the-fly = true

    on-init {
    }

    on-resource zImage {
        fat_rm(${BOOT_PART_OFFSET}, "zImage.pre")

        # write to the fat partition
        fat_write(${BOOT_PART_OFFSET}, "zImage.new")
    }

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

    on-finish {
        # Until this .fw is run, the system will still boot to the old firmware
        fw_create(${FINALIZE_FW})
        # Add an empty file to flag mbr-a to be written
        fw_add_local_file(${FINALIZE_FW}, "write.mbr-a", "/dev/null")
    }

    on-error {
        # Clean up in case something goes wrong
        fat_rm(${BOOT_PART_OFFSET}, "zImage.new")
        fat_rm(${BOOT_PART_OFFSET}, ${FINALIZE_FW})
    }
}

task upgrade.b {
    # This task upgrades the B partition
    require-partition1-offset = ${ROOTFS_A_PART_OFFSET}

    # Since the upgrade won't run until it has been finalized, it's ok
    # to write data as it is read.
    verify-on-the-fly = true

    on-init {
    }

    on-resource zImage {
        fat_rm(${BOOT_PART_OFFSET}, "zImage.pre")

        # write to the fat partition
        fat_write(${BOOT_PART_OFFSET}, "zImage.new")
    }

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

    on-finish {
        # Until this .fw is run, the system will still boot to the old firmware
        fw_create(${FINALIZE_FW})
        # Add an empty file to flag mbr-b to be written
        fw_add_local_file(${FINALIZE_FW}, "write.mbr-b", "/dev/null")
    }

    on-error {
        # Clean up in case something goes wrong
        fat_rm(${BOOT_PART_OFFSET}, "zImage.new")
        fat_rm(${BOOT_PART_OFFSET}, ${FINALIZE_FW})
    }
}

# This action is intended to be called from ${FINALIZE_FW} after the "upgrade" task
# has been run. It requires the "write.mbr-a" or "write.mbr-b" resources to exist
# and those don't exist until the "upgrade" task makes them.
task on-reboot {
    on-resource write.mbr-a {
        mbr_write(mbr-a)
    }
    on-resource write.mbr-b {
        mbr_write(mbr-b)
    }
    on-finish {
        fat_mv(${BOOT_PART_OFFSET}, "zImage", "zImage.pre")
        fat_mv(${BOOT_PART_OFFSET}, "zImage.new", "zImage")
    }
}
