summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorDavid Anderson <dvander@google.com>2022-05-12 11:32:53 -0500
committerAnand Gadiyar <gadiyar@ti.com>2022-05-12 11:49:16 -0500
commita6a7bd888383a313557ca0ed001aac09e92473db (patch)
tree96c53cbf3ff623695a9240653e8b5d5f28a79e60 /common
parentb7ab6e41f8ec83d58ec74ef7e9c22e42e86ab1a7 (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.c42
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.