diff options
author | Alex Deymo <deymo@google.com> | 2017-03-24 23:05:29 -0700 |
---|---|---|
committer | Alistair Strachan <astrachan@google.com> | 2018-08-10 19:54:55 -0700 |
commit | e322bcb9ab1d8c09196359eafe5021b4dae1cc19 (patch) | |
tree | d542098f3ca0079fd489346ba21d1e31b0c017d8 | |
parent | 0ec64604e347544b09428ef7badd6ed3554a40a6 (diff) | |
download | u-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/Kconfig | 20 | ||||
-rw-r--r-- | cmd/Makefile | 2 | ||||
-rw-r--r-- | cmd/android_ab_select.c | 56 | ||||
-rw-r--r-- | cmd/android_cmds.c | 74 | ||||
-rw-r--r-- | cmd/boot_android.c | 65 | ||||
-rw-r--r-- | cmd/load_android.c | 56 | ||||
-rw-r--r-- | common/Kconfig | 10 | ||||
-rw-r--r-- | common/Makefile | 1 | ||||
-rw-r--r-- | common/android_ab.c | 266 | ||||
-rw-r--r-- | common/image-android.c | 51 | ||||
-rw-r--r-- | include/android_ab.h | 33 | ||||
-rw-r--r-- | include/android_cmds.h | 32 | ||||
-rw-r--r-- | include/image.h | 19 |
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 */ /** |