aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Deymo <deymo@google.com>2017-03-24 23:05:29 -0700
committerAlistair Strachan <astrachan@google.com>2018-08-10 19:54:55 -0700
commite322bcb9ab1d8c09196359eafe5021b4dae1cc19 (patch)
treed542098f3ca0079fd489346ba21d1e31b0c017d8
parent0ec64604e347544b09428ef7badd6ed3554a40a6 (diff)
downloadu-boot-e322bcb9ab1d8c09196359eafe5021b4dae1cc19.tar.gz
android: Implement A/B slot select.
The android_bootloader_control struct defined in bootloader_message.h stored the A/B metadata used to decide which slot should we use to boot the device. This patch implements the bootloader side of the slot selection in a new "android_ab_select" command which decides the current slot and updates the metadata as needed. Bug: 32707546 Test: Booted a rpi3, updated to the other slot. Change-Id: I9344ff5b76194160d2b466a50e84f4f423b1a98a
-rw-r--r--cmd/Kconfig20
-rw-r--r--cmd/Makefile2
-rw-r--r--cmd/android_ab_select.c56
-rw-r--r--cmd/android_cmds.c74
-rw-r--r--cmd/boot_android.c65
-rw-r--r--cmd/load_android.c56
-rw-r--r--common/Kconfig10
-rw-r--r--common/Makefile1
-rw-r--r--common/android_ab.c266
-rw-r--r--common/image-android.c51
-rw-r--r--include/android_ab.h33
-rw-r--r--include/android_cmds.h32
-rw-r--r--include/image.h19
13 files changed, 624 insertions, 61 deletions
diff --git a/cmd/Kconfig b/cmd/Kconfig
index ef8d9cba0b..3c62f0a01d 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -607,6 +607,17 @@ config CMD_ADC
Shows ADC device info and permit printing one-shot analog converted
data from a named Analog to Digital Converter.
+config CMD_ANDROID_AB_SELECT
+ bool "android_ab_select"
+ default n
+ depends on ANDROID_AB
+ help
+ On Android devices with more than one boot slot (multiple copies of
+ the kernel and system images) this provides a command to select which
+ slot should be used to boot from and register the boot attempt. This
+ is used by the new A/B update model where one slot is updated in the
+ background while running from the other slot.
+
config CMD_BOOT_ANDROID
bool "boot_android"
default n
@@ -838,6 +849,15 @@ config CMD_LOADS
help
Load an S-Record file over serial line
+config CMD_LOAD_ANDROID
+ bool "load_android"
+ default n
+ depends on ANDROID_BOOT_IMAGE
+ help
+ Load an Android Boot image from storage. The Android Boot images
+ define the size and kernel address on the header, which are used by
+ this command.
+
config CMD_MMC
bool "mmc"
help
diff --git a/cmd/Makefile b/cmd/Makefile
index 95508385f6..abcfc64686 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -12,6 +12,7 @@ obj-y += version.o
# command
obj-$(CONFIG_CMD_AES) += aes.o
+obj-$(CONFIG_CMD_ANDROID_AB_SELECT) += android_ab_select.o android_cmds.o
obj-$(CONFIG_CMD_ADC) += adc.o
obj-$(CONFIG_CMD_ARMFLASH) += armflash.o
obj-y += blk_common.o
@@ -77,6 +78,7 @@ obj-$(CONFIG_LED_STATUS_CMD) += legacy_led.o
obj-$(CONFIG_CMD_LED) += led.o
obj-$(CONFIG_CMD_LICENSE) += license.o
obj-y += load.o
+obj-$(CONFIG_CMD_LOAD_ANDROID) += load_android.o android_cmds.o
obj-$(CONFIG_CMD_LOG) += log.o
obj-$(CONFIG_ID_EEPROM) += mac.o
obj-$(CONFIG_CMD_MD5SUM) += md5sum.o
diff --git a/cmd/android_ab_select.c b/cmd/android_ab_select.c
new file mode 100644
index 0000000000..2e3a3116ba
--- /dev/null
+++ b/cmd/android_ab_select.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <android_cmds.h>
+#include <android_ab.h>
+#include <common.h>
+#include <command.h>
+
+static int do_android_ab_select(cmd_tbl_t *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ int ret;
+ struct blk_desc *dev_desc;
+ disk_partition_t part_info;
+ char slot[2];
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ /* Lookup the "misc" partition from argv[2] and argv[3] */
+ if (part_get_info_by_dev_and_name_or_num(argv[2], argv[3],
+ &dev_desc, &part_info) < 0) {
+ return CMD_RET_FAILURE;
+ }
+
+ ret = android_ab_select(dev_desc, &part_info);
+ if (ret < 0) {
+ printf("Android boot failed, error %d.\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ /* Android standard slot names are 'a', 'b', ... */
+ slot[0] = ANDROID_BOOT_SLOT_NAME(ret);
+ slot[1] = '\0';
+ env_set(argv[1], slot);
+ printf("ANDROID: Booting slot: %s\n", slot);
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+ android_ab_select, 4, 0, do_android_ab_select,
+ "Select the slot used to boot from and register the boot attempt.",
+ "<slot_var_name> <interface> <dev[:part|;part_name]>\n"
+ " - Load the slot metadata from the partition 'part' on\n"
+ " device type 'interface' instance 'dev' and store the active\n"
+ " slot in the 'slot_var_name' variable. This also updates the\n"
+ " Android slot metadata with a boot attempt, which can cause\n"
+ " successive calls to this function to return a different result\n"
+ " if the returned slot runs out of boot attempts.\n"
+ " - If 'part_name' is passed, preceded with a ; instead of :, the\n"
+ " partition name whose label is 'part_name' will be looked up in\n"
+ " the partition table. This is commonly the \"misc\" partition.\n"
+);
diff --git a/cmd/android_cmds.c b/cmd/android_cmds.c
new file mode 100644
index 0000000000..95265773ae
--- /dev/null
+++ b/cmd/android_cmds.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <android_cmds.h>
+
+#include <common.h>
+#include <part.h>
+
+/**
+ * part_get_info_by_dev_and_name - Parse a device number and partition name
+ * string in the form of "device_num;partition_name", for example "0;misc".
+ * If the partition is found, sets dev_desc and part_info accordingly with the
+ * information of the partition with the given partition_name.
+ *
+ * @dev_iface: Device interface.
+ * @dev_part_str: Input string argument, like "0;misc".
+ * @dev_desc: Place to store the device description pointer.
+ * @part_info: Place to store the partition information.
+ * @return 0 on success, or -1 on error
+ */
+static int part_get_info_by_dev_and_name(const char *dev_iface,
+ const char *dev_part_str,
+ struct blk_desc **dev_desc,
+ disk_partition_t *part_info)
+{
+ char *ep;
+ const char *part_str;
+ int dev_num;
+
+ part_str = strchr(dev_part_str, ';');
+ if (!part_str || part_str == dev_part_str)
+ return -1;
+
+ dev_num = simple_strtoul(dev_part_str, &ep, 16);
+ if (ep != part_str) {
+ /* Not all the first part before the ; was parsed. */
+ return -1;
+ }
+ part_str++;
+
+ *dev_desc = blk_get_dev(dev_iface, dev_num);
+ if (!*dev_desc) {
+ printf("Could not find %s %d\n", dev_iface, dev_num);
+ return -1;
+ }
+ if (part_get_info_by_name(*dev_desc, part_str, part_info) < 0) {
+ printf("Could not find \"%s\" partition\n", part_str);
+ return -1;
+ }
+ return 0;
+}
+
+int part_get_info_by_dev_and_name_or_num(const char *dev_iface,
+ const char *dev_part_str,
+ struct blk_desc **dev_desc,
+ disk_partition_t *part_info) {
+ /* Split the part_name if passed as "$dev_num;part_name". */
+ if (!part_get_info_by_dev_and_name(dev_iface, dev_part_str,
+ dev_desc, part_info))
+ return 0;
+ /* Couldn't lookup by name, try looking up the partition description
+ * directly.
+ */
+ if (blk_get_device_part_str(dev_iface, dev_part_str,
+ dev_desc, part_info, 1) < 0) {
+ printf("Couldn't find partition %s %s\n",
+ dev_iface, dev_part_str);
+ return -1;
+ }
+ return 0;
+}
diff --git a/cmd/boot_android.c b/cmd/boot_android.c
index a2148e0238..0da6a7d056 100644
--- a/cmd/boot_android.c
+++ b/cmd/boot_android.c
@@ -5,53 +5,10 @@
*/
#include <android_bootloader.h>
+#include <android_cmds.h>
#include <common.h>
#include <command.h>
-/**
- * part_get_info_by_dev_and_name - Parse a device number and partition name
- * string in the form of "device_num;partition_name", for example "0;misc".
- * If the partition is found, sets dev_desc and part_info accordingly with the
- * information of the partition with the given partition_name.
- *
- * @dev_iface: Device interface.
- * @dev_part_str: Input string argument, like "0;misc".
- * @dev_desc: Place to put the device description pointer.
- * @part_info: Place to put the partition information.
- * @return 0 on success, or -1 on error
- */
-static int part_get_info_by_dev_and_name(const char *dev_iface,
- const char *dev_part_str,
- struct blk_desc **dev_desc,
- disk_partition_t *part_info)
-{
- char *ep;
- const char *part_str;
- int dev_num;
-
- part_str = strchr(dev_part_str, ';');
- if (!part_str)
- return -1;
-
- dev_num = simple_strtoul(dev_part_str, &ep, 16);
- if (ep != part_str) {
- /* Not all the first part before the ; was parsed. */
- return -1;
- }
- part_str++;
-
- *dev_desc = blk_get_dev(dev_iface, dev_num);
- if (!*dev_desc) {
- printf("Could not find %s %d\n", dev_iface, dev_num);
- return -1;
- }
- if (part_get_info_by_name(*dev_desc, part_str, part_info) < 0) {
- printf("Could not find \"%s\" partition\n", part_str);
- return -1;
- }
- return 0;
-}
-
static int do_boot_android(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
{
@@ -60,8 +17,6 @@ static int do_boot_android(cmd_tbl_t *cmdtp, int flag, int argc,
char *addr_arg_endp, *addr_str;
struct blk_desc *dev_desc;
disk_partition_t part_info;
- const char *misc_part_iface;
- const char *misc_part_desc;
if (argc < 4)
return CMD_RET_USAGE;
@@ -80,21 +35,9 @@ static int do_boot_android(cmd_tbl_t *cmdtp, int flag, int argc,
load_address = CONFIG_SYS_LOAD_ADDR;
}
- /* Lookup the "misc" partition from argv[1] and argv[2] */
- misc_part_iface = argv[1];
- misc_part_desc = argv[2];
- /* Split the part_name if passed as "$dev_num;part_name". */
- if (part_get_info_by_dev_and_name(misc_part_iface, misc_part_desc,
- &dev_desc, &part_info) < 0) {
- /* Couldn't lookup by name from mmc, try looking up the
- * partition description directly.
- */
- if (blk_get_device_part_str(misc_part_iface, misc_part_desc,
- &dev_desc, &part_info, 1) < 0) {
- printf("Couldn't find partition %s %s\n",
- misc_part_iface, misc_part_desc);
- return CMD_RET_FAILURE;
- }
+ if (part_get_info_by_dev_and_name_or_num(argv[1], argv[2],
+ &dev_desc, &part_info) < 0) {
+ return CMD_RET_FAILURE;
}
ret = android_bootloader_boot_flow(dev_desc, &part_info, argv[3],
diff --git a/cmd/load_android.c b/cmd/load_android.c
new file mode 100644
index 0000000000..e2ca4fe6ca
--- /dev/null
+++ b/cmd/load_android.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <common.h>
+#include <command.h>
+
+static int do_load_android(cmd_tbl_t *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ int boot_partition;
+ unsigned long load_address;
+ char *addr_arg_endp, *addr_str;
+ struct blk_desc *dev_desc;
+ disk_partition_t part_info;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+ if (argc > 4)
+ return CMD_RET_USAGE;
+
+ if (argc >= 4) {
+ load_address = simple_strtoul(argv[3], &addr_arg_endp, 16);
+ if (addr_arg_endp == argv[3] || *addr_arg_endp != '\0')
+ return CMD_RET_USAGE;
+ } else {
+ addr_str = env_get("loadaddr");
+ if (addr_str != NULL)
+ load_address = simple_strtoul(addr_str, NULL, 16);
+ else
+ load_address = CONFIG_SYS_LOAD_ADDR;
+ }
+
+ boot_partition = blk_get_device_part_str(argv[1],
+ (argc >= 3) ? argv[2] : NULL,
+ &dev_desc, &part_info, 1);
+ if (boot_partition < 0)
+ return CMD_RET_FAILURE;
+
+ if (android_image_load(dev_desc, &part_info, load_address, -1UL) < 0) {
+ printf("Error loading Android Image from %s %d:%d to 0x%lx.\n",
+ argv[1], dev_desc->devnum, boot_partition, load_address);
+ return CMD_RET_FAILURE;
+ }
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+ load_android, 4, 0, do_load_android,
+ "load Android Boot image from storage.",
+ "<interface> [<dev[:part]> [<addr>]]\n"
+ " - Load a binary Android Boot image from the partition 'part' on\n"
+ " device type 'interface' instance 'dev' to address 'addr'."
+);
diff --git a/common/Kconfig b/common/Kconfig
index 07f2350872..f68d929386 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -589,6 +589,16 @@ config ANDROID_BOOTLOADER
recovery mode or bootloader mode) and, if enabled, the slot to boot
from in devices with multiple boot slots (A/B devices).
+config ANDROID_AB
+ bool "Support for Android A/B updates"
+ default n
+ help
+ If enabled, adds support for the new Android A/B update model. This
+ allows the bootloader to select which slot to boot from based on the
+ information provided by userspace via the Android boot_ctrl HAL. This
+ 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.
+
menu "Start-up hooks"
config ARCH_EARLY_INIT_R
diff --git a/common/Makefile b/common/Makefile
index 6ae7842ef0..ccd497d161 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -99,6 +99,7 @@ obj-y += malloc_simple.o
endif
endif
obj-y += image.o
+obj-$(CONFIG_ANDROID_AB) += android_ab.o
obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o
obj-$(CONFIG_ANDROID_BOOTLOADER) += android_bootloader.o
obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o
diff --git a/common/android_ab.c b/common/android_ab.c
new file mode 100644
index 0000000000..4a690c8db3
--- /dev/null
+++ b/common/android_ab.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <android_ab.h>
+
+#include <android_bootloader_message.h>
+#include <common.h>
+#include <malloc.h>
+#include <u-boot/crc.h>
+
+/** android_boot_control_compute_crc - Compute the CRC-32 of the bootloader
+ * control struct. Only the bytes up to the crc32_le field are considered for
+ * the CRC-32 calculation.
+ */
+static uint32_t android_boot_control_compute_crc(
+ struct android_bootloader_control *abc)
+{
+ return crc32(0, (void *)abc, offsetof(typeof(*abc), crc32_le));
+}
+
+/** android_boot_control_default - Initialize android_bootloader_control to the
+ * default value which allows to boot all slots in order from the first one.
+ * This value should be used when the bootloader message is corrupted, but not
+ * when a valid message indicates that all slots are unbootable.
+ */
+void android_boot_control_default(struct android_bootloader_control *abc)
+{
+ int i;
+ const struct android_slot_metadata metadata = {
+ .priority = 15,
+ .tries_remaining = 7,
+ .successful_boot = 0,
+ .verity_corrupted = 0,
+ .reserved = 0
+ };
+ memcpy(abc->slot_suffix, "a\0\0\0", 4);
+ abc->magic = ANDROID_BOOT_CTRL_MAGIC;
+ abc->version = ANDROID_BOOT_CTRL_VERSION;
+ abc->nb_slot = ANDROID_NUM_SLOTS;
+ memset(abc->reserved0, 0, sizeof(abc->reserved0));
+ for (i = 0; i < abc->nb_slot; ++i) {
+ abc->slot_info[i] = metadata;
+ }
+ memset(abc->reserved1, 0, sizeof(abc->reserved1));
+ abc->crc32_le = android_boot_control_compute_crc(abc);
+}
+
+/** android_boot_control_create_from_disk
+ * Load the boot_control struct from disk into newly allocated memory. This
+ * function allocates and returns an integer number of disk blocks, based on the
+ * block size of the passed device to help performing a read-modify-write
+ * operation on the boot_control struct. The boot_control struct offset (2 KiB)
+ * must be a multiple of the device block size, for simplicity.
+ * @dev_desc: device where to read the boot_control struct from.
+ * @part_info: partition in 'dev_desc' where to read from, normally the "misc"
+ * partition should be used.
+ */
+static void *android_boot_control_create_from_disk(
+ struct blk_desc *dev_desc,
+ const disk_partition_t *part_info)
+{
+ ulong abc_offset, abc_blocks;
+ void *buf;
+
+ abc_offset = offsetof(struct android_bootloader_message_ab,
+ slot_suffix);
+ if (abc_offset % part_info->blksz) {
+ printf("ANDROID: Boot control block not block aligned.\n");
+ return NULL;
+ }
+ abc_offset /= part_info->blksz;
+
+ abc_blocks = DIV_ROUND_UP(sizeof(struct android_bootloader_control),
+ part_info->blksz);
+ if (abc_offset + abc_blocks > part_info->size) {
+ printf("ANDROID: boot control partition too small. Need at"
+ " least %lu blocks but have %lu blocks.\n",
+ abc_offset + abc_blocks, part_info->size);
+ return NULL;
+ }
+ buf = malloc(abc_blocks * part_info->blksz);
+ if (!buf)
+ return NULL;
+
+ if (blk_dread(dev_desc, part_info->start + abc_offset, abc_blocks,
+ buf) != abc_blocks) {
+ printf("ANDROID: Could not read from boot control partition\n");
+ free(buf);
+ return NULL;
+ }
+ debug("ANDROID: Loaded ABC, %lu blocks.\n", abc_blocks);
+ return buf;
+}
+
+/** android_boot_control_store
+ * Store the loaded boot_control block back to the same location it was read
+ * from with android_boot_control_create_from_misc().
+ *
+ * @abc_data_block: pointer to the boot_control struct and the extra bytes after
+ * it up to the nearest block boundary.
+ * @dev_desc: device where we should write the boot_control struct.
+ * @part_info: partition on the 'dev_desc' where to write.
+ * @return 0 on success and -1 on error.
+ */
+static int android_boot_control_store(void *abc_data_block,
+ struct blk_desc *dev_desc,
+ const disk_partition_t *part_info)
+{
+ ulong abc_offset, abc_blocks;
+
+ abc_offset = offsetof(struct android_bootloader_message_ab,
+ slot_suffix) / part_info->blksz;
+ abc_blocks = DIV_ROUND_UP(sizeof(struct android_bootloader_control),
+ part_info->blksz);
+ if (blk_dwrite(dev_desc, part_info->start + abc_offset, abc_blocks,
+ abc_data_block) != abc_blocks) {
+ printf("ANDROID: Could not write back the misc partition\n");
+ return -1;
+ }
+ return 0;
+}
+
+/** android_boot_compare_slots - compares two slots returning which slot is
+ * should we boot from among the two.
+ * @a: The first bootable slot metadata
+ * @b: The second bootable slot metadata
+ * @return negative if the slot "a" is better, positive of the slot "b" is
+ * better or 0 if they are equally good.
+ */
+static int android_ab_compare_slots(const struct android_slot_metadata *a,
+ const struct android_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;
+}
+
+int android_ab_select(struct blk_desc *dev_desc, disk_partition_t *part_info)
+{
+ struct android_bootloader_control *abc;
+ u32 crc32_le;
+ int slot, i;
+ bool store_needed = false;
+ char slot_suffix[4];
+
+ abc = android_boot_control_create_from_disk(dev_desc, part_info);
+ if (!abc) {
+ /* This condition represents an actual problem with the code
+ * or the board setup, like an invalid partition information.
+ * Signal a repair mode and do not try to boot from either
+ * slot.
+ */
+ return -1;
+ }
+
+ crc32_le = android_boot_control_compute_crc(abc);
+ if (abc->crc32_le != crc32_le) {
+ printf("ANDROID: Invalid CRC-32 (expected %.8x, found %.8x), "
+ "re-initializing A/B metadata.\n",
+ crc32_le, abc->crc32_le);
+ android_boot_control_default(abc);
+ store_needed = true;
+ }
+
+ if (abc->magic != ANDROID_BOOT_CTRL_MAGIC) {
+ printf("ANDROID: Unknown A/B metadata: %.8x\n", abc->magic);
+ free(abc);
+ return -1;
+ }
+
+ if (abc->version > ANDROID_BOOT_CTRL_VERSION) {
+ printf("ANDROID: Unsupported A/B metadata version: %.8x\n",
+ abc->version);
+ free(abc);
+ return -1;
+ }
+
+ /* At this point a valid boot control metadata is stored in abc,
+ * followed by other reserved data in the same block.
+ * We select a with the higher priority slot that
+ * - is not marked as corrupted and
+ * - either has tries_remaining > 0 or successful_boot is true.
+ * If the slot selected has a false successful_boot, we also decrement
+ * the tries_remaining until it eventually becomes unbootable because
+ * tries_remaining reaches 0. This mechanism produces a bootloader
+ * induced rollback, typically right after a failed update.
+ */
+
+ /* Safety check: limit the number of slots. */
+ if (abc->nb_slot > ARRAY_SIZE(abc->slot_info)) {
+ abc->nb_slot = ARRAY_SIZE(abc->slot_info);
+ store_needed = true;
+ }
+
+ slot = -1;
+ for (i = 0; i < abc->nb_slot; ++i) {
+ if (abc->slot_info[i].verity_corrupted ||
+ !abc->slot_info[i].tries_remaining) {
+ debug("ANDROID: unbootable slot %d tries: %d, "
+ "corrupt: %d\n",
+ i,
+ abc->slot_info[i].tries_remaining,
+ abc->slot_info[i].verity_corrupted);
+ continue;
+ }
+ debug("ANDROID: bootable slot %d pri: %d, tries: %d, "
+ "corrupt: %d, successful: %d\n",
+ i,
+ abc->slot_info[i].priority,
+ abc->slot_info[i].tries_remaining,
+ abc->slot_info[i].verity_corrupted,
+ abc->slot_info[i].successful_boot);
+
+ if (slot < 0 ||
+ android_ab_compare_slots(&abc->slot_info[i],
+ &abc->slot_info[slot]) < 0) {
+ slot = i;
+ }
+ }
+
+ if (slot >= 0 && !abc->slot_info[slot].successful_boot) {
+ printf("ANDROID: Attempting slot %c, tries remaining %d\n",
+ ANDROID_BOOT_SLOT_NAME(slot),
+ abc->slot_info[slot].tries_remaining);
+ abc->slot_info[slot].tries_remaining--;
+ store_needed = true;
+ }
+
+ if (slot >= 0) {
+ /* Legacy user-space requires this field to be set in the BCB.
+ * Newer releases load this the slot suffix from the command
+ * line or the device tree.
+ */
+ memset(slot_suffix, 0, sizeof(slot_suffix));
+ slot_suffix[0] = ANDROID_BOOT_SLOT_NAME(slot);
+ if (memcmp(abc->slot_suffix, slot_suffix,
+ sizeof(slot_suffix))) {
+ memcpy(abc->slot_suffix, slot_suffix,
+ sizeof(slot_suffix));
+ store_needed = true;
+ }
+ }
+
+ if (store_needed) {
+ abc->crc32_le = android_boot_control_compute_crc(abc);
+ android_boot_control_store(abc, dev_desc, part_info);
+ }
+ free(abc);
+
+ if (slot < 0)
+ return -1;
+ return slot;
+}
diff --git a/common/image-android.c b/common/image-android.c
index 2f38c191e9..c92e4b0126 100644
--- a/common/image-android.c
+++ b/common/image-android.c
@@ -7,6 +7,7 @@
#include <image.h>
#include <android_image.h>
#include <malloc.h>
+#include <mapmem.h>
#include <errno.h>
#define ANDROID_IMAGE_DEFAULT_KERNEL_ADDR 0x10008000
@@ -145,6 +146,56 @@ int android_image_get_ramdisk(const struct andr_img_hdr *hdr,
return 0;
}
+long android_image_load(struct blk_desc *dev_desc,
+ const disk_partition_t *part_info,
+ unsigned long load_address,
+ unsigned long max_size) {
+ void *buf;
+ long blk_cnt, blk_read = 0;
+
+ if (max_size < part_info->blksz)
+ return -1;
+
+ /* We don't know the size of the Android image before reading the header
+ * so we don't limit the size of the mapped memory.
+ */
+ buf = map_sysmem(load_address, 0 /* size */);
+
+ /* Read the Android header first and then read the rest. */
+ if (blk_dread(dev_desc, part_info->start, 1, buf) != 1)
+ blk_read = -1;
+
+ if (!blk_read && android_image_check_header(buf) != 0) {
+ printf("** Invalid Android Image header **\n");
+ blk_read = -1;
+ }
+ if (!blk_read) {
+ blk_cnt = (android_image_get_end(buf) - (ulong)buf +
+ part_info->blksz - 1) / part_info->blksz;
+ if (blk_cnt * part_info->blksz > max_size) {
+ debug("Android Image too big (%lu bytes, max %lu)\n",
+ android_image_get_end(buf) - (ulong)buf,
+ max_size);
+ blk_read = -1;
+ } else {
+ debug("Loading Android Image (%lu blocks) to 0x%lx... ",
+ blk_cnt, load_address);
+ blk_read = blk_dread(dev_desc, part_info->start,
+ blk_cnt, buf);
+ }
+ }
+
+ unmap_sysmem(buf);
+ if (blk_read < 0)
+ return blk_read;
+
+ debug("%lu blocks read: %s\n",
+ blk_read, (blk_read == blk_cnt) ? "OK" : "ERROR");
+ if (blk_read != blk_cnt)
+ return -1;
+ return blk_read;
+}
+
int android_image_get_second(const struct andr_img_hdr *hdr,
ulong *second_data, ulong *second_len)
{
diff --git a/include/android_ab.h b/include/android_ab.h
new file mode 100644
index 0000000000..e590f8fa33
--- /dev/null
+++ b/include/android_ab.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef __ANDROID_AB_H
+#define __ANDROID_AB_H
+
+#include <common.h>
+
+/* Android standard boot slot names are 'a', 'b', 'c', ... */
+#define ANDROID_BOOT_SLOT_NAME(slot_num) ('a' + (slot_num))
+
+/* Number of slots */
+#define ANDROID_NUM_SLOTS 2
+
+/** android_ab_select - Select the slot where to boot from.
+ * On Android devices with more than one boot slot (multiple copies of the
+ * kernel and system images) selects which slot should be used to boot from and
+ * registers the boot attempt. This is used in by the new A/B update model where
+ * one slot is updated in the background while running from the other slot. If
+ * the selected slot did not successfully boot in the past, a boot attempt is
+ * registered before returning from this function so it isn't selected
+ * indefinitely.
+ *
+ * @dev_desc: Place to store the device description pointer.
+ * @part_info: Place to store the partition information.
+ * @return the slot number (0-based) on success, or -1 on error.
+ */
+int android_ab_select(struct blk_desc *dev_desc, disk_partition_t *part_info);
+
+#endif
diff --git a/include/android_cmds.h b/include/android_cmds.h
new file mode 100644
index 0000000000..94df916835
--- /dev/null
+++ b/include/android_cmds.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef __ANDROID_CMDS_H
+#define __ANDROID_CMDS_H
+
+#include <common.h>
+
+/**
+ * part_get_info_by_dev_and_name_or_num - Parse a device number and partition
+ * description (either name or number) in the form of device number plus
+ * partition name separated by a ";" (like "device_num;partition_name") or
+ * a device number plus a partition number separated by a ":". For example both
+ * "0;misc" and "0:1" can be valid partition descriptions for a given interface.
+ * If the partition is found, sets dev_desc and part_info accordingly with the
+ * information of the partition.
+ *
+ * @dev_iface: Device interface.
+ * @dev_part_str: Input partition description, like "0;misc" or "0:1".
+ * @dev_desc: Place to store the device description pointer.
+ * @part_info: Place to store the partition information.
+ * @return 0 on success, or -1 on error
+ */
+int part_get_info_by_dev_and_name_or_num(const char *dev_iface,
+ const char *dev_part_str,
+ struct blk_desc **dev_desc,
+ disk_partition_t *part_info);
+
+#endif
diff --git a/include/image.h b/include/image.h
index 420b8ff576..8a73bc2f8f 100644
--- a/include/image.h
+++ b/include/image.h
@@ -1289,6 +1289,25 @@ ulong android_image_get_end(const struct andr_img_hdr *hdr);
ulong android_image_get_kload(const struct andr_img_hdr *hdr);
void android_print_contents(const struct andr_img_hdr *hdr);
+/** android_image_load - Load an Android Image from storage.
+ *
+ * Load an Android Image based on the header size in the storage. Return the
+ * number of bytes read from storage, which could be bigger than the actual
+ * Android Image as described in the header size. In case of error reading the
+ * image or if the image size needed to be read from disk is bigger than the
+ * the passed |max_size| a negative number is returned.
+ *
+ * @dev_desc: The device where to read the image from
+ * @part_info: The partition in |dev_desc| where to read the image from
+ * @load_address: The address where the image will be loaded
+ * @max_size: The maximum loaded size, in bytes
+ * @return the number of bytes read or a negative number in case of error.
+ */
+long android_image_load(struct blk_desc *dev_desc,
+ const disk_partition_t *part_info,
+ unsigned long load_address,
+ unsigned long max_size);
+
#endif /* CONFIG_ANDROID_BOOT_IMAGE */
/**