diff options
author | David Anderson <dvander@google.com> | 2022-05-12 11:32:53 -0500 |
---|---|---|
committer | Anand Gadiyar <gadiyar@ti.com> | 2022-05-12 11:49:16 -0500 |
commit | a6a7bd888383a313557ca0ed001aac09e92473db (patch) | |
tree | 96c53cbf3ff623695a9240653e8b5d5f28a79e60 /common | |
parent | b7ab6e41f8ec83d58ec74ef7e9c22e42e86ab1a7 (diff) |
common: android_ab: do not attempt to round-robin bootable slots.
This feature does not quite fit within the A/B flow.
The intent of A/B is to provide an automatic rollback option for broken
OTAs. Once an OTA has been applied, the slot may not boot for a number
of reasons (power loss, broken package, etc), and it is important to
make consistent attempts to boot to the new slot rather than find *a*
bootable slot (otherwise, the update may not take).
Note that once a slot has been marked bootable, encryption keys are
upgraded, and old slots will not work. Trying to rotate between slots
is not likely to succeed.
Note that Android ensures that the active slot always has the
highest priority. In the current u-boot implementation, this affords no
possibility of rollback.
To match the expected A/B flow, this patch makes the following changes:
- When initializing the BCB, set the "_a" slot to have the highest
priority.
- Pick the highest priority slot that has been marked successful OR has
boot tries remaining.
- If no such slot exists, the system is not bootable.
Link: https://android-review.googlesource.com/c/platform/external/u-boot/+/1446442
Signed-off-by: Guillaume La Roque <glaroque@baylibre.com>
Diffstat (limited to 'common')
-rw-r--r-- | common/android_ab.c | 42 |
1 files changed, 28 insertions, 14 deletions
diff --git a/common/android_ab.c b/common/android_ab.c index 80aedfbef8..a1f1e4f327 100644 --- a/common/android_ab.c +++ b/common/android_ab.c @@ -59,9 +59,17 @@ static int ab_control_default(struct bootloader_control *abc) abc->version = BOOT_CTRL_VERSION; abc->nb_slot = NUM_SLOTS; memset(abc->reserved0, 0, sizeof(abc->reserved0)); - for (i = 0; i < abc->nb_slot; ++i) + for (i = 0; i < abc->nb_slot; ++i) { abc->slot_info[i] = metadata; + /* One slot should always have higher priority than other slots, + * otherwise we can ping-pong between slots based on tries_remaining. + * Since the default slot is _a, make _a highest priority. + */ + if (i != 0) + abc->slot_info[i].priority = metadata.priority - 1; + } + memset(abc->reserved1, 0, sizeof(abc->reserved1)); abc->crc32_le = ab_control_compute_crc(abc); @@ -153,6 +161,11 @@ static int ab_control_store(struct blk_desc *dev_desc, return 0; } +static bool is_slot_bootable(const struct slot_metadata* slot) +{ + return slot->tries_remaining > 0 || slot->successful_boot; +} + /** * Compare two slots. * @@ -166,19 +179,14 @@ static int ab_control_store(struct blk_desc *dev_desc, static int ab_compare_slots(const struct slot_metadata *a, const struct slot_metadata *b) { - /* Higher priority is better */ - if (a->priority != b->priority) - return b->priority - a->priority; - - /* Higher successful_boot value is better, in case of same priority */ - if (a->successful_boot != b->successful_boot) - return b->successful_boot - a->successful_boot; - - /* Higher tries_remaining is better to ensure round-robin */ - if (a->tries_remaining != b->tries_remaining) - return b->tries_remaining - a->tries_remaining; - - return 0; + /* Pick the highest priority slot that can be considered bootable. */ + if (a->priority > b->priority && is_slot_bootable(a)) + return -1; + if (b->priority > a->priority && is_slot_bootable(b)) + return 1; + + /* The higher priority slot is not bootable, so pick the slot that is. */ + return b->successful_boot - a->successful_boot; } int ab_select_slot(struct blk_desc *dev_desc, struct disk_partition *part_info, @@ -269,6 +277,12 @@ int ab_select_slot(struct blk_desc *dev_desc, struct disk_partition *part_info, } } + /* Fail to boot normally if there is no bootable slot. */ + if (normal_boot && !is_slot_bootable(&abc->slot_info[slot])) { + log_err("ANDROID: No bootable slot was found.\n"); + return -EINVAL; + } + /* Note that we only count the boot attempt as a valid try when performing * normal boots to Android. Booting to recovery or fastboot does not count * as a normal boot. |