rpi5: recovery as a separate on-demand image; A/B on tryboot

- mkbootimg.mk: boot.img uses the normal ramdisk again; the recovery.img rule
  is guarded so it only builds when recovery is enabled (PAWLET_BUILD_RECOVERY=1)
  and carries the recovery ramdisk.
- config.txt [tryboot] back to boot_partition=2 (A/B trial); recovery enters
  via reboot,3.
- vendor.prop reboot reason back to reboot,tryboot.
- drop the no-op reserved-size lines (AOSP ignores RESERVED_SIZE for fixed
  non-dynamic partitions).
This commit is contained in:
oxmc
2026-07-17 05:11:12 -07:00
parent a77d3465a6
commit cd30b06245
4 changed files with 43 additions and 52 deletions
+9 -11
View File
@@ -28,15 +28,15 @@ TARGET_NO_BOOTLOADER := true
# dlsym()s make_device() from whatever this points at). See
# vendor/pawlet/recovery_ui/README.md and NOTES-ota-recovery-ab.md.
TARGET_NO_RECOVERY := false
# Recovery ramdisk is embedded in the boot partition (mkbootimg.mk copies
# ramdisk-recovery.img in as ramdisk.img). Normal boot sets force_normal_boot=1
# in cmdline.txt so init boots Android; recovery is entered via the [tryboot]
# cmdline swap (see boot/config.txt). Do NOT use BOARD_USES_RECOVERY_AS_BOOT: it
# makes AOSP seize the boot.img rule and collide with our custom FAT mkbootimg.mk.
# BOARD_USES_FULL_RECOVERY_IMAGE avoids the recovery-from-boot imgdiff /
# recovery-resource.dat path. mkbootimg.mk also builds a standalone recovery.img
# (satisfies INSTALLED_RECOVERYIMAGE_TARGET; a fallback, not flashed by default
# since recovery lives in boot).
# Recovery is a SEPARATE image on the recovery partition, built on demand (see
# PRODUCT_BUILD_RECOVERY_IMAGE in common.mk / the `precovery` helper). It is kept
# out of the normal build so AOSP does not force recovery.img into the vendor
# image size check (RECOVERY_FROM_BOOT_PATCH), which would overflow the padded
# 2GB vendor partition. mkbootimg.mk builds the recovery FAT when enabled;
# pawlet-recoveryboot.sh enters it via reboot,3. BOARD_USES_FULL_RECOVERY_IMAGE
# avoids the recovery-from-boot imgdiff / recovery-resource.dat path. Do NOT use
# BOARD_USES_RECOVERY_AS_BOOT: it makes AOSP seize the boot.img rule and collide
# with our custom FAT mkbootimg.mk.
BOARD_USES_FULL_RECOVERY_IMAGE := true
# BOARD_RECOVERYIMAGE_PARTITION_SIZE (128M) comes from BoardConfigPawlet.mk.
TARGET_RECOVERY_FSTAB := device/pawlet/rpi5/recovery/recovery.rpi5.fstab
@@ -77,13 +77,11 @@ PRODUCT_MANIFEST_FILES := $(DEVICE_PATH)/product_manifest.xml
BOARD_FLASH_BLOCK_SIZE := 4096
BOARD_BOOTIMAGE_PARTITION_SIZE := 134217728 # 128M
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 8589934592 # 8192M (8GB)
BOARD_SYSTEMIMAGE_PARTITION_RESERVED_SIZE := 104857600 # 100MB
BOARD_SYSTEM_EXTIMAGE_PARTITION_SIZE := 4294967296 # 4096M (4GB)
BOARD_SYSTEM_EXTIMAGE_PARTITION_RESERVED_SIZE := 104857600 # 100MB
BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE := ext4
TARGET_COPY_OUT_SYSTEM_EXT := system_ext
BOARD_VENDORIMAGE_PARTITION_SIZE := 2147483648 # 2048M (2GB)
BOARD_VENDORIMAGE_PARTITION_RESERVED_SIZE := 104857600 # 100MB
BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := ext4
TARGET_COPY_OUT_VENDOR := vendor
BOARD_USES_METADATA_PARTITION := true
+10 -12
View File
@@ -38,16 +38,14 @@ dtoverlay=dwc2,dr_mode=otg
[all]
# Recovery entry. The recovery ramdisk is embedded in this boot partition
# (mkbootimg.mk stages ramdisk-recovery.img as ramdisk.img); the normal
# cmdline.txt sets androidboot.force_normal_boot=1 so first-stage init
# boots Android. pawlet-recoveryboot.sh issues `reboot tryboot` when the misc BCB
# requests recovery — this one-shot swaps in cmdline_recovery.txt (no
# force_normal_boot), so the same boot partition comes up in recovery mode.
#
# A/B slot switching does NOT use tryboot: the updater boots the candidate slot
# via a one-shot `reboot,2` (partition 2 = boot_b). On confirmed success the app
# copies boot_b -> boot_a via /dev/block/by-name/misc to make B permanent.
# See vendor.prop ro.vendor.pawlet.ota.reboot_reason.
# A/B boot slot selection via RPi tryboot.
# Slot A = partition 1 (boot_a) — normal boot, no override needed.
# Slot B = partition 2 (boot_b) — booted once via `reboot tryboot` (the updater)
# to trial an update; the firmware reverts to slot A if the trial fails
# to confirm, so a bad update can't wedge the device. On success the app
# copies boot_b -> boot_a (via /dev/block/by-name/misc) to make it
# permanent.
# Recovery is a separate image on partition 3, entered via `reboot,3`
# (pawlet-recoveryboot.sh) — not tryboot.
[tryboot]
cmdline=cmdline_recovery.txt
boot_partition=2
+21 -26
View File
@@ -8,8 +8,9 @@
DEVICE_PATH := device/pawlet/rpi5
KERNEL_PATH := device/brcm/rpi5-kernel
# The recovery ramdisk is embedded in the boot partition (see BoardConfig.mk).
# Inside the FAT it is named ramdisk.img (config.txt: initramfs ramdisk.img).
# Recovery is a separate image, built on demand (PRODUCT_BUILD_RECOVERY_IMAGE,
# via the `precovery` helper) and flashed to the recovery partition. The normal
# build only produces boot.img.
RECOVERY_RAMDISK := $(PRODUCT_OUT)/ramdisk-recovery.img
# Use a stamp file so ninja tracks the directory contents correctly.
@@ -17,48 +18,42 @@ RECOVERY_RAMDISK := $(PRODUCT_OUT)/ramdisk-recovery.img
RPI_BOOT_OUT := $(PRODUCT_OUT)/rpiboot
RPI_BOOT_STAMP := $(RPI_BOOT_OUT)/.stamp
# Shared boot-partition staging: kernel, DTBs, overlays, config.txt, and the
# recovery ramdisk. cmdline.txt is NOT staged here — each image rule writes its
# own (force_normal_boot for boot, absent for recovery) so the boot and recovery
# images can build in parallel without racing on it.
$(RPI_BOOT_STAMP): $(RECOVERY_RAMDISK)
# Shared boot-partition staging: kernel, DTBs, overlays, config.txt. The ramdisk
# and cmdline.txt are added per-image in the rules below, so a stray
# cmdline/ramdisk in the staging dir can never collide with the per-image mcopy
# (which would hang on mtools' interactive overwrite prompt).
$(RPI_BOOT_STAMP): $(INSTALLED_RAMDISK_TARGET)
rm -rf $(RPI_BOOT_OUT)
mkdir -p $(RPI_BOOT_OUT)/overlays
cp $(DEVICE_PATH)/boot/* $(RPI_BOOT_OUT)
cp $(KERNEL_PATH)/Image $(RPI_BOOT_OUT)
cp $(KERNEL_PATH)/bcm2712*-rpi-*.dtb $(RPI_BOOT_OUT)
cp $(KERNEL_PATH)/overlays/* $(RPI_BOOT_OUT)/overlays
cp $(RECOVERY_RAMDISK) $(RPI_BOOT_OUT)/ramdisk.img
# cmdline.txt is written per-image (boot vs recovery) in the rules below; make
# sure none rides along in the staging dir, or the per-image mcopy would hit
# an interactive overwrite prompt and hang the build.
rm -f $(RPI_BOOT_OUT)/cmdline.txt $(RPI_BOOT_OUT)/cmdline_recovery.txt
rm -f $(RPI_BOOT_OUT)/cmdline*.txt $(RPI_BOOT_OUT)/ramdisk*.img
touch $@
# Normal boot partition. cmdline.txt sets androidboot.force_normal_boot=1 so
# first-stage init boots Android even though ramdisk.img is the recovery
# ramdisk; cmdline_recovery.txt (no force_normal_boot) rides along for the
# [tryboot] recovery swap in config.txt.
$(INSTALLED_BOOTIMAGE_TARGET): $(RPI_BOOT_STAMP)
# Normal boot partition: the standard Android ramdisk + cmdline.
$(INSTALLED_BOOTIMAGE_TARGET): $(RPI_BOOT_STAMP) $(INSTALLED_RAMDISK_TARGET)
$(call pretty,"Target boot image: $@")
echo "$(BOARD_KERNEL_CMDLINE) androidboot.force_normal_boot=1" > $@.cmdline
echo "$(BOARD_KERNEL_CMDLINE)" > $@.cmdline_recovery
echo "$(BOARD_KERNEL_CMDLINE)" > $@.cmdline
dd if=/dev/zero of=$@ bs=1M count=128
mkfs.fat -F 32 -n "BOOT" $@
mcopy -s -i $@ $(RPI_BOOT_OUT)/* ::
mcopy -i $@ $(INSTALLED_RAMDISK_TARGET) ::ramdisk.img
mcopy -i $@ $@.cmdline ::cmdline.txt
mcopy -i $@ $@.cmdline_recovery ::cmdline_recovery.txt
rm -f $@.cmdline $@.cmdline_recovery
rm -f $@.cmdline
# Standalone recovery image. Not flashed by default (recovery lives in the boot
# partition); built to satisfy INSTALLED_RECOVERYIMAGE_TARGET and usable as a
# manual fallback. Its cmdline omits force_normal_boot, so booting it goes
# straight into recovery.
$(INSTALLED_RECOVERYIMAGE_TARGET): $(RPI_BOOT_STAMP)
# Recovery partition image — only defined when recovery is enabled
# (PAWLET_BUILD_RECOVERY=1 -> INSTALLED_RECOVERYIMAGE_TARGET set). Same FAT
# layout but carrying the recovery ramdisk. Flash it to the recovery partition.
ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
$(INSTALLED_RECOVERYIMAGE_TARGET): $(RPI_BOOT_STAMP) $(RECOVERY_RAMDISK)
$(call pretty,"Target recovery image: $@")
echo "$(BOARD_KERNEL_CMDLINE)" > $@.cmdline
dd if=/dev/zero of=$@ bs=1M count=128
mkfs.fat -F 32 -n "RECOVERY" $@
mcopy -s -i $@ $(RPI_BOOT_OUT)/* ::
mcopy -i $@ $(RECOVERY_RAMDISK) ::ramdisk.img
mcopy -i $@ $@.cmdline ::cmdline.txt
rm -f $@.cmdline
endif
+3 -3
View File
@@ -37,9 +37,9 @@ persist.vendor.hdmi.cec_device=cec0
ro.hdmi.cec_device_types=playback_device
ro.hdmi.device_type=4
# A/B OTA: boot the candidate slot (boot_b = partition 2) via a one-shot RPi
# partition reboot. tryboot is reserved for recovery entry (see config.txt).
ro.vendor.pawlet.ota.reboot_reason=reboot,2
# A/B OTA: boot the candidate slot via RPi tryboot — a safe trial that reverts
# to the active slot if the candidate fails to boot.
ro.vendor.pawlet.ota.reboot_reason=reboot,tryboot
# Chipset
ro.soc.manufacturer=Broadcom