Skip to content

Commit

Permalink
TRYBOOT WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
fhunleth committed Dec 1, 2024
1 parent 15acc8d commit 3ddf6f5
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 107 deletions.
102 changes: 39 additions & 63 deletions fwup-ops.conf
Original file line number Diff line number Diff line change
Expand Up @@ -21,48 +21,6 @@ require-fwup-version="1.0.0"

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

mbr mbr-a {
partition 0 {
block-offset = ${BOOT_A_PART_OFFSET}
block-count = ${BOOT_A_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 = ${APP_PART_OFFSET}
block-count = ${APP_PART_COUNT}
type = 0x83 # Linux
expand = true
}
# partition 3 is unused
}

mbr mbr-b {
partition 0 {
block-offset = ${BOOT_B_PART_OFFSET}
block-count = ${BOOT_B_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 = ${APP_PART_OFFSET}
block-count = ${APP_PART_COUNT}
type = 0x83 # Linux
expand = true
}
# partition 3 is unused
}

# 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.
Expand Down Expand Up @@ -90,28 +48,31 @@ task factory-reset {
##
task prevent-revert.a {
# Check that we're running on B
require-partition-offset(0, ${BOOT_B_PART_OFFSET})
require-partition-offset(1, ${ROOTFS_B_PART_OFFSET})
require-uboot-variable(uboot-env, "nerves_fw_active", "b")

on-init {
on-resource autoboot-b.txt {
info("Preventing reverts to partition A")
# Ensure that autoboot is booting the B partition
fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt")
uboot_setenv(uboot-env, "nerves_fw_validated", "1")
# Remove U-Boot variables that fwup uses to allow reverting images
uboot_unsetenv(uboot-env, "a.nerves_fw_platform")
uboot_unsetenv(uboot-env, "a.nerves_fw_architecture")
uboot_setenv(uboot-env, "nerves_fw_validated", "1")
# Clear out the old image using TRIM. This requires --enable-trim
trim(${ROOTFS_A_PART_OFFSET}, ${ROOTFS_A_PART_COUNT})
trim(${BOOT_A_PART_OFFSET}, ${BOOT_A_PART_COUNT})
}
}
task prevent-revert.b {
# Check that we're running on A
require-partition-offset(0, ${BOOT_A_PART_OFFSET})
require-partition-offset(1, ${ROOTFS_A_PART_OFFSET})
require-uboot-variable(uboot-env, "nerves_fw_active", "a")

on-init {
on-resource autoboot-a.txt {
info("Preventing reverts to partition B")
# Ensure that autoboot is booting the A partition
fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt")
uboot_setenv(uboot-env, "nerves_fw_validated", "1")
# Remove U-Boot variables that fwup uses to allow reverting images
uboot_unsetenv(uboot-env, "b.nerves_fw_platform")
uboot_unsetenv(uboot-env, "b.nerves_fw_architecture")
Expand All @@ -131,39 +92,37 @@ task prevent-revert.fail {
##
task revert.a {
# This task reverts to the A partition, so check that we're running on B
require-partition-offset(0, ${BOOT_B_PART_OFFSET})
require-partition-offset(1, ${ROOTFS_B_PART_OFFSET})
require-uboot-variable(uboot-env, "nerves_fw_active", "b")

# Verify that partition A has 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 {
on-resource autoboot-a.txt {
info("Reverting to partition A")

# Switch over
# Update the autoboot first and then the tracking U-Boot variables
fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt")
uboot_setenv(uboot-env, "nerves_fw_active", "a")
mbr_write(mbr-a)
uboot_setenv(uboot-env, "nerves_fw_validated", "1")
}
}

task revert.b {
# This task reverts to the B partition, so check that we're running on A
require-partition-offset(0, ${BOOT_A_PART_OFFSET})
require-partition-offset(1, ${ROOTFS_A_PART_OFFSET})
require-path-at-offset("/", ${ROOTFS_A_PART_OFFSET})
require-uboot-variable(uboot-env, "nerves_fw_active", "a")

# Verify that partition B has 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 {
on-resource autoboot-b.txt {
info("Reverting to partition B")

# Switch over
# Update the autoboot first and then the tracking U-Boot variables
fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt")
uboot_setenv(uboot-env, "nerves_fw_active", "b")
mbr_write(mbr-b)
uboot_setenv(uboot-env, "nerves_fw_validated", "1")
}
}

Expand Down Expand Up @@ -222,11 +181,28 @@ task status.fail {
##
# validate
#
# The fwup configuration for this device always validates, so this doesn't do anything.
# Update the autoboot.txt file to unconditionally boot the active partition.
##
task validate {
on-init {
info("Validate")
task validate.a {
require-path-at-offset("/", ${ROOTFS_A_PART_OFFSET})
require-uboot-variable(uboot-env, "nerves_fw_active", "a")

on-resource autoboot-a.txt {
info("Validate A")
fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt")
uboot_setenv(uboot-env, "nerves_fw_validated", "1")
}
}
task validate.b {
require-path-at-offset("/", ${ROOTFS_B_PART_OFFSET})
require-uboot-variable(uboot-env, "nerves_fw_active", "b")

on-resource autoboot-b.txt {
info("Validate B")
fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt")
uboot_setenv(uboot-env, "nerves_fw_validated", "1")
}
}
task validate.fail {
on-init { error("Can't validate unless running from active partition") }
}
64 changes: 36 additions & 28 deletions fwup.conf
Original file line number Diff line number Diff line change
Expand Up @@ -75,46 +75,45 @@ file-resource hifiberry-dac.dtbo {
host-path = "${NERVES_SYSTEM}/images/rpi-firmware/overlays/hifiberry-dac.dtbo"
}

mbr mbr-a {
mbr mbr {
partition 0 {
block-offset = ${BOOT_A_PART_OFFSET}
block-count = ${BOOT_A_PART_COUNT}
block-offset = ${AUTOBOOT_PART_OFFSET}
block-count = ${AUTOBOOT_PART_COUNT}
type = 0xc # FAT32
boot = true
}
partition 1 {
block-offset = ${ROOTFS_A_PART_OFFSET}
block-count = ${ROOTFS_A_PART_COUNT}
type = 0x83 # Linux
block-offset = ${BOOT_A_PART_OFFSET}
block-count = ${BOOT_A_PART_COUNT}
type = 0xc # FAT32
boot = true
}
partition 2 {
block-offset = ${APP_PART_OFFSET}
block-count = ${APP_PART_COUNT}
type = 0x83 # Linux
expand = true
}
# partition 3 is unused
}

mbr mbr-b {
partition 0 {
block-offset = ${BOOT_B_PART_OFFSET}
block-count = ${BOOT_B_PART_COUNT}
type = 0xc # FAT32
boot = true
}
partition 1 {
partition 3 {
type = 0xf # Extended partition
block-offset = ${EXTENDED_PART_OFFSET}
}
partition 4 {
block-offset = ${ROOTFS_A_PART_OFFSET}
block-count = ${ROOTFS_A_PART_COUNT}
type = 0x83 # Linux
}
partition 5 {
block-offset = ${ROOTFS_B_PART_OFFSET}
block-count = ${ROOTFS_B_PART_COUNT}
type = 0x83 # Linux
}
partition 2 {
partition 6 {
block-offset = ${APP_PART_OFFSET}
block-count = ${APP_PART_COUNT}
type = 0x83 # Linux
expand = true
}
# partition 3 is unused
}

# Location where installed firmware information is stored.
Expand All @@ -131,7 +130,7 @@ task complete {
require-unmounted-destination = true

on-init {
mbr_write(mbr-a)
mbr_write(mbr)

fat_mkfs(${BOOT_A_PART_OFFSET}, ${BOOT_A_PART_COUNT})
fat_setlabel(${BOOT_A_PART_OFFSET}, "BOOT-A")
Expand All @@ -157,6 +156,11 @@ task complete {
uboot_setenv(uboot-env, "a.nerves_fw_uuid", "\${FWUP_META_UUID}")
}

on-resource autoboot-a.txt {
fat_mkfs(${AUTOBOOT_PART_OFFSET}, ${AUTOBOOT_PART_COUNT})
fat_setlabel(${AUTOBOOT_PART_OFFSET}, "AUTOBOOT")
fat_write(${AUTOBOOT_PART_OFFSET}, "autoboot.txt")
}
on-resource config.txt { fat_write(${BOOT_A_PART_OFFSET}, "config.txt") }
on-resource cmdline.txt { fat_write(${BOOT_A_PART_OFFSET}, "cmdline.txt") }
on-resource bootcode.bin { fat_write(${BOOT_A_PART_OFFSET}, "bootcode.bin") }
Expand Down Expand Up @@ -200,7 +204,11 @@ task complete {

task upgrade.a {
# This task upgrades the A partition
require-partition-offset(1, ${ROOTFS_B_PART_OFFSET})
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}")
Expand All @@ -226,9 +234,7 @@ task upgrade.a {
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.
# Write the new boot partition files and rootfs.
on-resource config.txt { fat_write(${BOOT_A_PART_OFFSET}, "config.txt") }
on-resource cmdline.txt { fat_write(${BOOT_A_PART_OFFSET}, "cmdline.txt") }
on-resource bootcode.bin { fat_write(${BOOT_A_PART_OFFSET}, "bootcode.bin") }
Expand Down Expand Up @@ -273,16 +279,19 @@ task upgrade.a {

# Switch over to boot the new firmware
uboot_setenv(uboot-env, "nerves_fw_active", "a")
mbr_write(mbr-a)
}

on-error {
}
}

task upgrade.b {
# This task upgrades the B partition
require-partition-offset(1, ${ROOTFS_A_PART_OFFSET})
# This task upgrades the A partition
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}")
Expand Down Expand Up @@ -353,7 +362,6 @@ task upgrade.b {

# Switch over to boot the new firmware
uboot_setenv(uboot-env, "nerves_fw_active", "b")
mbr_write(mbr-b)
}

on-error {
Expand Down
56 changes: 40 additions & 16 deletions fwup_include/fwup-common.conf
Original file line number Diff line number Diff line change
Expand Up @@ -33,41 +33,46 @@ define(ROOTFS, "${NERVES_SYSTEM}/images/rootfs.squashfs")
# | Firmware configuration data|
# | (formatted as uboot env) |
# +----------------------------+
# | p0*: Boot A (FAT32) |
# | p0: Autoboot (FAT32) |
# +----------------------------+
# | p1: Boot A (FAT32) |
# | zImage, bootcode.bin, |
# | config.txt, etc. |
# +----------------------------+
# | p0*: Boot B (FAT32) |
# | p2: Boot B (FAT32) |
# +----------------------------+
# | p1*: Rootfs A (squashfs) |
# | p3: Extended partition |
# +----------------------------+
# | p1*: Rootfs B (squashfs) |
# | p4: Rootfs A (squashfs) |
# +----------------------------+
# | p2: Application (ext4) |
# | p5: Rootfs B (squashfs) |
# +----------------------------+
# | p6: Application (ext4) |
# +----------------------------+
#
# The p0/p1 partition points to whichever of configurations A or B that is
# active.
#
# The image is sized to be less than 1 GB so that it fits on nearly any SDCard
# around. If you have a larger SDCard and need more space, feel free to bump
# the partition sizes below.

# 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.
# (Sizes are in 512 byte blocks)
define(UBOOT_ENV_OFFSET, 16)
define(UBOOT_ENV_COUNT, 16) # 8 KB

define(BOOT_A_PART_OFFSET, 63)
define(BOOT_A_PART_COUNT, 38630)
# Even though non-FAT32 partitions appear to work, make sure all partitions are
# at least 65,525 blocks long to force FAT32. See
# https://forums.raspberrypi.com/viewtopic.php?p=1505893&hilit=genimage#p1506378..
define(AUTOBOOT_PART_OFFSET, 128)
define(AUTOBOOT_PART_COUNT, 65536)

define-eval(BOOT_A_PART_OFFSET, "${AUTOBOOT_PART_OFFSET} + ${AUTOBOOT_PART_COUNT}")
define(BOOT_A_PART_COUNT, 65536)
define-eval(BOOT_B_PART_OFFSET, "${BOOT_A_PART_OFFSET} + ${BOOT_A_PART_COUNT}")
define(BOOT_B_PART_COUNT, ${BOOT_A_PART_COUNT})
define-eval(EXTENDED_PART_OFFSET, "${BOOT_B_PART_OFFSET} + ${BOOT_B_PART_COUNT}")

# Let the rootfs have room to grow up to 128 MiB and align it to the nearest 1
# Let the rootfs have room to grow up to 256 MiB and align it to the nearest 1
# MB boundary
define(ROOTFS_A_PART_OFFSET, 77324)
define(ROOTFS_A_PART_COUNT, 289044)
define(ROOTFS_A_PART_OFFSET, 198656)
define(ROOTFS_A_PART_COUNT, 524288)
define-eval(ROOTFS_B_PART_OFFSET, "${ROOTFS_A_PART_OFFSET} + ${ROOTFS_A_PART_COUNT}")
define(ROOTFS_B_PART_COUNT, ${ROOTFS_A_PART_COUNT})

Expand All @@ -85,3 +90,22 @@ meta-architecture = ${NERVES_FW_ARCHITECTURE}
meta-author = ${NERVES_FW_AUTHOR}
meta-vcs-identifier = ${NERVES_FW_VCS_IDENTIFIER}
meta-misc = ${NERVES_FW_MISC}

file-resource autoboot-a.txt {
contents = "\
[all]\n\
tryboot_a_b=1\n\
boot_partition=2\n\
[tryboot]\n\
boot_partition=3\n\
\n"
}
file-resource autoboot-b.txt {
contents = "\
[all]\n\
tryboot_a_b=1\n\
boot_partition=3\n\
[tryboot]\n\
boot_partition=2\n\
\n"
}

0 comments on commit 3ddf6f5

Please sign in to comment.