Skip to content

Commit

Permalink
android_ab: Try backup booloader_message
Browse files Browse the repository at this point in the history
Some devices keep 2 copies of the bootloader_message in the misc
partition and write each in sequence when updating. This ensures that
there is always one valid copy of the bootloader_message. Teach u-boot
to optionally try a backup bootloader_message from a specified offset if
the primary one fails its CRC check.

Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
  • Loading branch information
JPEWdev authored and trini committed Jul 17, 2023
1 parent 55a4244 commit 3430f24
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 12 deletions.
77 changes: 65 additions & 12 deletions boot/android_ab.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -200,29 +206,63 @@ 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;
}

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;
}
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions common/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 6 additions & 0 deletions doc/android/ab.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------------

Expand Down

0 comments on commit 3430f24

Please sign in to comment.