diff options
-rw-r--r-- | boot/android_ab.c | 77 | ||||
-rw-r--r-- | common/Kconfig | 9 | ||||
-rw-r--r-- | doc/android/ab.rst | 6 |
3 files changed, 80 insertions, 12 deletions
diff --git a/boot/android_ab.c b/boot/android_ab.c index 60ae002978f..73b55c196c4 100644 --- a/boot/android_ab.c +++ b/boot/android_ab.c @@ -85,11 +85,13 @@ static int ab_control_default(struct bootloader_control *abc) */ static int ab_control_create_from_disk(struct blk_desc *dev_desc, const struct disk_partition *part_info, - struct bootloader_control **abc) + struct bootloader_control **abc, + ulong offset) { ulong abc_offset, abc_blocks, ret; - abc_offset = offsetof(struct bootloader_message_ab, slot_suffix); + abc_offset = offset + + offsetof(struct bootloader_message_ab, slot_suffix); if (abc_offset % part_info->blksz) { log_err("ANDROID: Boot control block not block aligned.\n"); return -EINVAL; @@ -135,11 +137,12 @@ static int ab_control_create_from_disk(struct blk_desc *dev_desc, */ static int ab_control_store(struct blk_desc *dev_desc, const struct disk_partition *part_info, - struct bootloader_control *abc) + struct bootloader_control *abc, ulong offset) { ulong abc_offset, abc_blocks, ret; - abc_offset = offsetof(struct bootloader_message_ab, slot_suffix) / + abc_offset = offset + + offsetof(struct bootloader_message_ab, slot_suffix) / part_info->blksz; abc_blocks = DIV_ROUND_UP(sizeof(struct bootloader_control), part_info->blksz); @@ -189,8 +192,11 @@ int ab_select_slot(struct blk_desc *dev_desc, struct disk_partition *part_info, int slot, i, ret; bool store_needed = false; char slot_suffix[4]; +#if ANDROID_AB_BACKUP_OFFSET + struct bootloader_control *backup_abc = NULL; +#endif - ret = ab_control_create_from_disk(dev_desc, part_info, &abc); + ret = ab_control_create_from_disk(dev_desc, part_info, &abc, 0); if (ret < 0) { /* * This condition represents an actual problem with the code or @@ -200,22 +206,53 @@ int ab_select_slot(struct blk_desc *dev_desc, struct disk_partition *part_info, return ret; } +#if ANDROID_AB_BACKUP_OFFSET + ret = ab_control_create_from_disk(dev_desc, part_info, &backup_abc, + ANDROID_AB_BACKUP_OFFSET); + if (ret < 0) { + free(abc); + return ret; + } +#endif + crc32_le = ab_control_compute_crc(abc); if (abc->crc32_le != crc32_le) { log_err("ANDROID: Invalid CRC-32 (expected %.8x, found %.8x),", crc32_le, abc->crc32_le); - log_err("re-initializing A/B metadata.\n"); - - ret = ab_control_default(abc); - if (ret < 0) { - free(abc); - return -ENODATA; +#if ANDROID_AB_BACKUP_OFFSET + crc32_le = ab_control_compute_crc(backup_abc); + if (backup_abc->crc32_le != crc32_le) { + log_err("ANDROID: Invalid backup CRC-32 ") + log_err("expected %.8x, found %.8x),", + crc32_le, backup_abc->crc32_le); +#endif + + log_err("re-initializing A/B metadata.\n"); + + ret = ab_control_default(abc); + if (ret < 0) { +#if ANDROID_AB_BACKUP_OFFSET + free(backup_abc); +#endif + free(abc); + return -ENODATA; + } +#if ANDROID_AB_BACKUP_OFFSET + } else { + /* + * Backup is valid. Copy it to the primary + */ + memcpy(abc, backup_abc, sizeof(*abc)); } +#endif store_needed = true; } if (abc->magic != BOOT_CTRL_MAGIC) { log_err("ANDROID: Unknown A/B metadata: %.8x\n", abc->magic); +#if ANDROID_AB_BACKUP_OFFSET + free(backup_abc); +#endif free(abc); return -ENODATA; } @@ -223,6 +260,9 @@ int ab_select_slot(struct blk_desc *dev_desc, struct disk_partition *part_info, if (abc->version > BOOT_CTRL_VERSION) { log_err("ANDROID: Unsupported A/B metadata version: %.8x\n", abc->version); +#if ANDROID_AB_BACKUP_OFFSET + free(backup_abc); +#endif free(abc); return -ENODATA; } @@ -297,8 +337,21 @@ int ab_select_slot(struct blk_desc *dev_desc, struct disk_partition *part_info, if (store_needed) { abc->crc32_le = ab_control_compute_crc(abc); - ab_control_store(dev_desc, part_info, abc); + ab_control_store(dev_desc, part_info, abc, 0); } + +#if ANDROID_AB_BACKUP_OFFSET + /* + * If the backup doesn't match the primary, write the primary + * to the backup offset + */ + if (memcmp(backup_abc, abc, sizeof(*abc)) != 0) { + ab_control_store(dev_desc, part_info, abc, + ANDROID_AB_BACKUP_OFFSET); + } + free(backup_abc); +#endif + free(abc); if (slot < 0) diff --git a/common/Kconfig b/common/Kconfig index 42baca20a61..f5ad63ce166 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -936,6 +936,15 @@ config ANDROID_AB allows a bootloader to try a new version of the system but roll back to previous version if the new one didn't boot all the way. +config ANDROID_AB_BACKUP_OFFSET + hex "Offset of backup bootloader control" + depends on ANDROID_AB + default 0x0 + help + If non-zero, a backup bootloader message starting at this offset in + the partition will tried in the event that the primary one (starting + at offset 0) fails its checksum. + endmenu menu "Blob list" diff --git a/doc/android/ab.rst b/doc/android/ab.rst index 961895c32e8..2adf88781d6 100644 --- a/doc/android/ab.rst +++ b/doc/android/ab.rst @@ -31,6 +31,12 @@ boot script. This command analyzes and processes A/B metadata stored on a special partition (e.g. ``misc``) and determines which slot should be used for booting up. +If the A/B metadata partition has a backup bootloader_message block that is used +to ensure one is always valid even in the event of interruption when writing, it +can be enabled in your board configuration file:: + + CONFIG_ANDROID_AB_BACKUP_OFFSET=0x1000 + Command usage ------------- |