[U-Boot] [PATCH v4 0/6] android: implement A/B boot process

*** This patch series depends on [4], [5], [6] and should be applied after these paticular patches.
This patch series adds support for Android A/B boot process [1]. Main steps of A/B boot process are: - A/B metadata integrity check - looking for the current slot (where the system should be booting from) - getting the name of the current boot partition (boot_a or boot_b) and loading the corresponding Android boot image - getting the name of the current system partition (system_a or system_b) and passing of its full name via kernel command line (like 'root=/dev/mmcblk1p11') - passing current slot via kernel command line (like 'androidboot.slot_suffix=_a') and via A/B metadata (e.g. via misc partition) - A/B metadata processing: setting the boot success flag for current slot, handling the retry counter, etc
A/B metadata is organized according to Android reference [2] and stored on 'misc' partition. On the first A/B boot process, when 'misc' partition doesn't contain required data, default A/B metadata will be created and stored in 'misc' partition. In the end of the Android boot, 'update_verifier' and 'update_engine' services are processing the A/B metadata through the Boot Control HAL. To confirm the boot was successful using current slot, "boot success" flag must be set on Android side.
To enable Android A/B support in U-Boot: 1. Set the following config options:
CONFIG_ANDROID_AB=y CONFIG_CMD_AB_SELECT=y
2. Change the disk layout so that it has sloted boot partitions. E.g. instead of 'boot' and 'system' partitions there should be 'boot_a', 'boot_b', 'system_a' and 'system_b' partitions.
To be able to actually test this patch series, the A/B features must be implemented and enabled in Android as well (see [1] for details).
Documentation and corresponding test for A/B boot is present here. The last patch in this series integrates A/B boot support on AM57xx based boards (though it's not enabled by default). Future users of A/B boot feature can use it as a reference.
This series is a part of previous submission [3] by Alex Deymo. It contains only A/B feature that was stripped out from there with some modifications for using with "bootm" command preferred in upstream.
Changes in v4: * Removed android_bl_msg.h and re-used the one, which was introduced by Eugeniu Rosca in [4] and [5]. * Initially we had intention to use "bcb" command and scripting for providing proper boot sequence logic (slot selection, decrementing the counter for each slot), but found out that it's better to keep using a dedicated command ab_select to avoid huge amount of scripting in the headers in includes/config for each platform. * Removed ANDROID_ prefix in multiple macros. * Addressed comments from Simon
Changes in v3: * Minor fixes in the ab metadata handling (added additional sanity checks). * As Ruslan Trofymenko left Linaro and won't address comments anymore, continue (added my S-b tag) upstreaming patches on my own.
Changes in v2: * 'android_ab_select' command is renamed to 'ab_select' command and moved to separate 'Android support commands' menu * For am57xx boards slotted sections (e.g. system_a and system_b) are added to the default sections if CONFIG_CMD_AB_SELECT flag is defined * Returned function error codes are clarified (errno using) * Some types constants and files are renamed * Assertion condition is clarified in test case * 'debug' calls are changed to 'log_debug' * The Guide is clarified by the results of changes
[1] https://source.android.com/devices/tech/ota/ab/ab_implement [2] bootable/recovery/bootloader_message/include/bootloader_message/bootloader_message.h [3] https://lists.denx.de/pipermail/u-boot/2017-April/285841.html [4] https://patchwork.ozlabs.org/patch/1080394/ [5] https://patchwork.ozlabs.org/patch/1104243/ [6] https://patchwork.ozlabs.org/patch/1116012/
Ruslan Trofymenko (6): disk: part: Extend API to get partition info common: Implement A/B metadata cmd: Add 'ab_select' command test/py: Add base test case for A/B updates doc: android: Add simple guide for A/B updates env: am57xx: Implement A/B boot process
cmd/Kconfig | 15 ++ cmd/Makefile | 1 + cmd/ab_select.c | 52 ++++++ common/Kconfig | 10 ++ common/Makefile | 1 + common/android_ab.c | 298 ++++++++++++++++++++++++++++++++++ configs/sandbox_defconfig | 2 + disk/part.c | 68 ++++++++ doc/README.android-ab | 67 ++++++++ include/android_ab.h | 34 ++++ include/environment/ti/boot.h | 58 ++++++- include/part.h | 21 +++ test/py/tests/test_ab.py | 74 +++++++++ 13 files changed, 695 insertions(+), 6 deletions(-) create mode 100644 cmd/ab_select.c create mode 100644 common/android_ab.c create mode 100644 doc/README.android-ab create mode 100644 include/android_ab.h create mode 100644 test/py/tests/test_ab.py

From: Ruslan Trofymenko ruslan.trofymenko@linaro.org
This patch adds part_get_info_by_dev_and_name_or_num() function which allows us to get partition info from its number or name. Partition of interest is specified by string like "device_num:partition_number" or "device_num#partition_name".
The patch was extracted from [1].
[1] https://android-review.googlesource.com/c/platform/external/u-boot/+/729880/...
Signed-off-by: Ruslan Trofymenko ruslan.trofymenko@linaro.org Signed-off-by: Igor Opaniuk igor.opaniuk@gmail.com Reviewed-by: Alistair Strachan astrachan@google.com Reviewed-by: Sam Protsenko semen.protsenko@linaro.org Reviewed-by: Simon Glass sjg@chromium.org ---
Changes in v4: None Changes in v3: None Changes in v2: * Error codes are changed to -EINVAL instead of -1
disk/part.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/part.h | 21 ++++++++++++++++ 2 files changed, 89 insertions(+)
diff --git a/disk/part.c b/disk/part.c index f14bc22b6d..7e84214731 100644 --- a/disk/part.c +++ b/disk/part.c @@ -674,6 +674,74 @@ int part_get_info_by_name(struct blk_desc *dev_desc, const char *name, return part_get_info_by_name_type(dev_desc, name, info, PART_TYPE_ALL); }
+/** + * Get partition info from device number and partition 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. + * + * @param[in] dev_iface Device interface + * @param[in] dev_part_str Input string argument, like "0#misc" + * @param[out] dev_desc Place to store the device description pointer + * @param[out] part_info Place to store the partition information + * @return 0 on success, or a negative 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 -EINVAL; + + dev_num = simple_strtoul(dev_part_str, &ep, 16); + if (ep != part_str) { + /* Not all the first part before the # was parsed. */ + return -EINVAL; + } + 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 -EINVAL; + } + if (part_get_info_by_name(*dev_desc, part_str, part_info) < 0) { + printf("Could not find "%s" partition\n", part_str); + return -EINVAL; + } + 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 -EINVAL; + } + return 0; +} + void part_set_generic_name(const struct blk_desc *dev_desc, int part_num, char *name) { diff --git a/include/part.h b/include/part.h index ebca546db5..0b5cf3d5e8 100644 --- a/include/part.h +++ b/include/part.h @@ -201,6 +201,27 @@ int part_get_info_by_name_type(struct blk_desc *dev_desc, const char *name, int part_get_info_by_name(struct blk_desc *dev_desc, const char *name, disk_partition_t *info);
+/** + * Get partition info from dev number + part name, or dev number + part number. + * + * 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. + * + * @param[in] dev_iface Device interface + * @param[in] dev_part_str Input partition description, like "0#misc" or "0:1" + * @param[out] dev_desc Place to store the device description pointer + * @param[out] part_info Place to store the partition information + * @return 0 on success, or a negative 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); + /** * part_set_generic_name() - create generic partition like hda1 or sdb2 *

From: Ruslan Trofymenko ruslan.trofymenko@linaro.org
This patch determines the A/B-specific bootloader message structure that is the basis for implementation of recovery and A/B update functions. A/B metadata is stored in this structure and used to decide which slot should we use to boot the device. Also some basic functions for A/B metadata manipulation are implemented (like slot selection).
The patch was extracted from commits [1], [2] with some coding style fixes.
[1] https://android-review.googlesource.com/c/platform/external/u-boot/+/729878/... [2] https://android-review.googlesource.com/c/platform/external/u-boot/+/729880/...
Signed-off-by: Ruslan Trofymenko ruslan.trofymenko@linaro.org Signed-off-by: Igor Opaniuk igor.opaniuk@gmail.com Reviewed-by: Sam Protsenko semen.protsenko@linaro.org Reviewed-by: Simon Glass sjg@chromium.org --- Changes in v4: * Removed android_bl_msg.h and re-used the one, which was introduced by Eugeniu Rosca in [4] and [5]. * Initially we had intention to use "bcb" command and scripting for providing proper boot sequence logic (slot selection, decrementing the counter for each slot), but found out that it's better to keep using a dedicated command ab_select to avoid huge amount of scripting in the headers in includes/config for each platform. * Remove ANDROID_ prefix for multiple macros
Changes in v3: * Add multiple sanity checks * Fix mix. minor code formatting issues
Changes in v2: * Function return codes are clarified * Some types and constants are renamed (for compactness) * android_bootloader_message.h is renamed to android_bl_msg.h * 'debug' calls are changed to 'log_debug' * Order of headers is changed * android_bl_msg.h was synced with AOSP master counterpart
common/Kconfig | 10 ++ common/Makefile | 1 + common/android_ab.c | 298 +++++++++++++++++++++++++++++++++++++++++++ include/android_ab.h | 34 +++++ 4 files changed, 343 insertions(+) create mode 100644 common/android_ab.c create mode 100644 include/android_ab.h
diff --git a/common/Kconfig b/common/Kconfig index af66496e75..63287865ef 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -814,6 +814,16 @@ config UPDATE_TFTP_MSEC_MAX default 100 depends on UPDATE_TFTP
+config ANDROID_AB + bool "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. + endmenu
menu "Blob list" diff --git a/common/Makefile b/common/Makefile index c7e41ef307..302d8beaf3 100644 --- a/common/Makefile +++ b/common/Makefile @@ -107,6 +107,7 @@ endif endif
obj-y += image.o +obj-$(CONFIG_ANDROID_AB) += android_ab.o obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o diff --git a/common/android_ab.c b/common/android_ab.c new file mode 100644 index 0000000000..43a1ec8c37 --- /dev/null +++ b/common/android_ab.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2017 The Android Open Source Project + */ +#include <common.h> +#include <android_ab.h> +#include <android_bootloader_message.h> +#include <linux/err.h> +#include <memalign.h> +#include <u-boot/crc.h> + +/** + * 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. + * + * @param[in] abc bootloader control block + * + * @return crc32 sum + */ +static uint32_t ab_control_compute_crc(struct bootloader_control *abc) +{ + return crc32(0, (void *)abc, offsetof(typeof(*abc), crc32_le)); +} + +/** + * Initialize bootloader_control to the default value. + * + * It allows us 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. + * + * @param[in] abc bootloader control block + * + * @return 0 on success and a negative on error + */ +static int ab_control_default(struct bootloader_control *abc) +{ + int i; + const struct slot_metadata metadata = { + .priority = 15, + .tries_remaining = 7, + .successful_boot = 0, + .verity_corrupted = 0, + .reserved = 0 + }; + + if (!abc) + return -EFAULT; + + memcpy(abc->slot_suffix, "a\0\0\0", 4); + abc->magic = BOOT_CTRL_MAGIC; + 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) + abc->slot_info[i] = metadata; + + memset(abc->reserved1, 0, sizeof(abc->reserved1)); + abc->crc32_le = ab_control_compute_crc(abc); + + return 0; +} + +/** + * 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. + * + * @param[in] dev_desc Device where to read the boot_control struct from + * @param[in] part_info Partition in 'dev_desc' where to read from, normally + * the "misc" partition should be used + * @param[out] pointer to pointer to bootloader_control data + * @return 0 on success and a negative on error + */ +static int ab_control_create_from_disk(struct blk_desc *dev_desc, + const disk_partition_t *part_info, + struct bootloader_control **abc) +{ + ulong abc_offset, abc_blocks; + + abc_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; + } + abc_offset /= part_info->blksz; + + abc_blocks = DIV_ROUND_UP(sizeof(struct bootloader_control), + part_info->blksz); + if (abc_offset + abc_blocks > part_info->size) { + log_err("ANDROID: boot control partition too small. Need at"); + log_err(" least %lu blocks but have %lu blocks.\n", + abc_offset + abc_blocks, part_info->size); + return -EINVAL; + } + *abc = malloc_cache_aligned(abc_blocks * part_info->blksz); + if (!*abc) + return -ENOMEM; + + if (blk_dread(dev_desc, part_info->start + abc_offset, abc_blocks, + *abc) != abc_blocks) { + log_err("ANDROID: Could not read from boot ctrl partition\n"); + free(*abc); + return -EIO; + } + + log_debug("ANDROID: Loaded ABC, %lu blocks\n", abc_blocks); + + return 0; +} + +/** + * Store the loaded boot_control block. + * + * Store back to the same location it was read from with + * ab_control_create_from_misc(). + * + * @param[in] dev_desc Device where we should write the boot_control struct + * @param[in] part_info Partition on the 'dev_desc' where to write + * @param[in] abc Pointer to the boot control struct and the extra bytes after + * it up to the nearest block boundary + * @return 0 on success and a negative on error + */ +static int ab_control_store(struct blk_desc *dev_desc, + const disk_partition_t *part_info, + struct bootloader_control *abc) +{ + ulong abc_offset, abc_blocks; + + abc_offset = offsetof(struct bootloader_message_ab, slot_suffix) / + part_info->blksz; + abc_blocks = DIV_ROUND_UP(sizeof(struct bootloader_control), + part_info->blksz); + if (blk_dwrite(dev_desc, part_info->start + abc_offset, abc_blocks, + abc) != abc_blocks) { + log_err("ANDROID: Could not write back the misc partition\n"); + return -EIO; + } + + return 0; +} + +/** + * Compare two slots. + * + * The function determines slot which is should we boot from among the two. + * + * @param[in] a The first bootable slot metadata + * @param[in] 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 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; +} + +int ab_select_slot(struct blk_desc *dev_desc, disk_partition_t *part_info) +{ + struct bootloader_control *abc = NULL; + u32 crc32_le; + int slot, i, ret; + bool store_needed = false; + char slot_suffix[4]; + + ret = ab_control_create_from_disk(dev_desc, part_info, &abc); + if (ret < 0) { + /* + * 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 ret; + } + + 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; + } + store_needed = true; + } + + if (abc->magic != BOOT_CTRL_MAGIC) { + log_err("ANDROID: Unknown A/B metadata: %.8x\n", abc->magic); + free(abc); + return -ENODATA; + } + + if (abc->version > BOOT_CTRL_VERSION) { + log_err("ANDROID: Unsupported A/B metadata version: %.8x\n", + abc->version); + free(abc); + return -ENODATA; + } + + /* + * 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 selected slot 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) { + log_debug("ANDROID: unbootable slot %d tries: %d, ", + i, abc->slot_info[i].tries_remaining); + log_debug("corrupt: %d\n", + abc->slot_info[i].verity_corrupted); + continue; + } + log_debug("ANDROID: bootable slot %d pri: %d, tries: %d, ", + i, abc->slot_info[i].priority, + abc->slot_info[i].tries_remaining); + log_debug("corrupt: %d, successful: %d\n", + abc->slot_info[i].verity_corrupted, + abc->slot_info[i].successful_boot); + + if (slot < 0 || + ab_compare_slots(&abc->slot_info[i], + &abc->slot_info[slot]) < 0) { + slot = i; + } + } + + if (slot >= 0 && !abc->slot_info[slot].successful_boot) { + log_err("ANDROID: Attempting slot %c, tries remaining %d\n", + 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 slot suffix from the command line + * or the device tree. + */ + memset(slot_suffix, 0, sizeof(slot_suffix)); + slot_suffix[0] = 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 = ab_control_compute_crc(abc); + ab_control_store(dev_desc, part_info, abc); + } + free(abc); + + if (slot < 0) + return -EINVAL; + + return slot; +} diff --git a/include/android_ab.h b/include/android_ab.h new file mode 100644 index 0000000000..810906d22b --- /dev/null +++ b/include/android_ab.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2017 The Android Open Source Project + */ + +#ifndef __ANDROID_AB_H +#define __ANDROID_AB_H + +#include <common.h> + +/* Android standard boot slot names are 'a', 'b', 'c', ... */ +#define BOOT_SLOT_NAME(slot_num) ('a' + (slot_num)) + +/* Number of slots */ +#define NUM_SLOTS 2 + +/** + * 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. + * + * @param[in] dev_desc Place to store the device description pointer + * @param[in] part_info Place to store the partition information + * @return The slot number (>= 0) on success, or a negative on error + */ +int ab_select_slot(struct blk_desc *dev_desc, disk_partition_t *part_info); + +#endif /* __ANDROID_AB_H */

From: Ruslan Trofymenko ruslan.trofymenko@linaro.org
For A/B system update support the Android boot process requires to send 'androidboot.slot_suffix' parameter as a command line argument. This patch implementes 'ab_select' command which allows us to obtain current slot by processing the A/B metadata.
The patch was extracted from commit [1] with one modification: the separator for specifying the name of metadata partition was changed from ';' to '#', because ';' is used for commands separation.
[1] https://android-review.googlesource.com/c/platform/external/u-boot/+/729880/...
Signed-off-by: Ruslan Trofymenko ruslan.trofymenko@linaro.org Signed-off-by: Igor Opaniuk igor.opaniuk@gmail.com Reviewed-by: Alistair Strachan astrachan@google.com Reviewed-by: Sam Protsenko semen.protsenko@linaro.org Reviewed-by: Simon Glass sjg@chromium.org --- Changes in v4: * Remove ANDROID prefix in BOOT_SLOT_NAME macro Changes in v3: None Changes in v2: * 'android_ab_select' command is renamed to 'ab_select' command * command is moved to the separate 'Android support commands' menu
cmd/Kconfig | 15 ++++++++++++++ cmd/Makefile | 1 + cmd/ab_select.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 cmd/ab_select.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index 3afb760a81..380edd0136 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1179,6 +1179,21 @@ config CMD_SETEXPR
endmenu
+menu "Android support commands" + +config CMD_AB_SELECT + bool "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. + +endmenu + if NET
menuconfig CMD_NET diff --git a/cmd/Makefile b/cmd/Makefile index 49e64cde1d..500147a2ac 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_AB_SELECT) += ab_select.o obj-$(CONFIG_CMD_ADC) += adc.o obj-$(CONFIG_CMD_ARMFLASH) += armflash.o obj-y += blk_common.o diff --git a/cmd/ab_select.c b/cmd/ab_select.c new file mode 100644 index 0000000000..7c8f2ee8eb --- /dev/null +++ b/cmd/ab_select.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2017 The Android Open Source Project + */ + +#include <android_ab.h> +#include <command.h> + +static int do_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 = ab_select_slot(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] = 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(ab_select, 4, 0, do_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" +);

From: Ruslan Trofymenko ruslan.trofymenko@linaro.org
Add sandbox test for 'ab_select' command.
Test: ./test/py/test.py --bd sandbox --build -k test_ab
Signed-off-by: Ruslan Trofymenko ruslan.trofymenko@linaro.org Signed-off-by: Igor Opaniuk igor.opaniuk@gmail.com Reviewed-by: Alistair Strachan astrachan@google.com Reviewed-by: Sam Protsenko semen.protsenko@linaro.org Reviewed-by: Simon Glass sjg@chromium.org --- Changes in v4: None Changes in v3: None
Changes in v2: * Changes related to command renaming (android_ab_select->ab_select). * Assertion condition was clarified. Full command output is controlled.
configs/sandbox_defconfig | 2 ++ test/py/tests/test_ab.py | 74 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 test/py/tests/test_ab.py
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 6894262b89..fd51a75b01 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -20,6 +20,7 @@ CONFIG_PRE_CON_BUF_ADDR=0xf0000 CONFIG_LOG_MAX_LEVEL=6 CONFIG_LOG_ERROR_RETURN=y CONFIG_DISPLAY_BOARDINFO_LATE=y +CONFIG_ANDROID_AB=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y CONFIG_CMD_BOOTZ=y @@ -47,6 +48,7 @@ CONFIG_CMD_REMOTEPROC=y CONFIG_CMD_SPI=y CONFIG_CMD_USB=y CONFIG_CMD_AXI=y +CONFIG_CMD_AB_SELECT=y CONFIG_CMD_TFTPPUT=y CONFIG_CMD_TFTPSRV=y CONFIG_CMD_RARP=y diff --git a/test/py/tests/test_ab.py b/test/py/tests/test_ab.py new file mode 100644 index 0000000000..b90ca87a02 --- /dev/null +++ b/test/py/tests/test_ab.py @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: GPL-2.0 +# (C) Copyright 2018 Texas Instruments, <www.ti.com> + +# Test A/B update commands. + +import os +import pytest +import u_boot_utils + +class ABTestDiskImage(object): + """Disk Image used by the A/B tests.""" + + def __init__(self, u_boot_console): + """Initialize a new ABTestDiskImage object. + + Args: + u_boot_console: A U-Boot console. + + Returns: + Nothing. + """ + + filename = 'test_ab_disk_image.bin' + + persistent = u_boot_console.config.persistent_data_dir + '/' + filename + self.path = u_boot_console.config.result_dir + '/' + filename + + with u_boot_utils.persistent_file_helper(u_boot_console.log, persistent): + if os.path.exists(persistent): + u_boot_console.log.action('Disk image file ' + persistent + + ' already exists') + else: + u_boot_console.log.action('Generating ' + persistent) + fd = os.open(persistent, os.O_RDWR | os.O_CREAT) + os.ftruncate(fd, 524288) + os.close(fd) + cmd = ('sgdisk', persistent) + u_boot_utils.run_and_log(u_boot_console, cmd) + + cmd = ('sgdisk', '--new=1:64:512', '-c 1:misc', persistent) + u_boot_utils.run_and_log(u_boot_console, cmd) + cmd = ('sgdisk', '-l', persistent) + u_boot_utils.run_and_log(u_boot_console, cmd) + + cmd = ('cp', persistent, self.path) + u_boot_utils.run_and_log(u_boot_console, cmd) + +di = None +@pytest.fixture(scope='function') +def ab_disk_image(u_boot_console): + global di + if not di: + di = ABTestDiskImage(u_boot_console) + return di + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('android_ab') +@pytest.mark.buildconfigspec('cmd_ab_select') +@pytest.mark.requiredtool('sgdisk') +def test_ab(ab_disk_image, u_boot_console): + """Test the 'ab_select' command.""" + + u_boot_console.run_command('host bind 0 ' + ab_disk_image.path) + + output = u_boot_console.run_command('ab_select slot_name host 0#misc') + assert 're-initializing A/B metadata' in output + assert 'Attempting slot a, tries remaining 7' in output + output = u_boot_console.run_command('printenv slot_name') + assert 'slot_name=a' in output + + output = u_boot_console.run_command('ab_select slot_name host 0:1') + assert 'Attempting slot b, tries remaining 7' in output + output = u_boot_console.run_command('printenv slot_name') + assert 'slot_name=b' in output

Hi Igor,
On Tue, Jul 2, 2019 at 11:51 AM Igor Opaniuk igor.opaniuk@gmail.com wrote:
From: Ruslan Trofymenko ruslan.trofymenko@linaro.org
Add sandbox test for 'ab_select' command.
Test: ./test/py/test.py --bd sandbox --build -k test_ab
Signed-off-by: Ruslan Trofymenko ruslan.trofymenko@linaro.org Signed-off-by: Igor Opaniuk igor.opaniuk@gmail.com Reviewed-by: Alistair Strachan astrachan@google.com Reviewed-by: Sam Protsenko semen.protsenko@linaro.org Reviewed-by: Simon Glass sjg@chromium.org
Changes in v4: None Changes in v3: None
Changes in v2:
- Changes related to command renaming (android_ab_select->ab_select).
- Assertion condition was clarified. Full command output is controlled.
configs/sandbox_defconfig | 2 ++ test/py/tests/test_ab.py | 74 +++++++++++++++++++++++++++++++++++++++
1. Can we please move it to test/py/tests/test_android/test_ab.py? This way we can run all android related tests if we want to, and test namespace won't get cluttered. 2. It's better to use long options for sgdisk tool. I found out that AOSP sgdisk doesn't work with short options. This change [1] fixed the problem for me, and it works with both AOSP and system version of sgdisk now. Can you please incorporate it in next version?
2 files changed, 76 insertions(+) create mode 100644 test/py/tests/test_ab.py
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 6894262b89..fd51a75b01 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -20,6 +20,7 @@ CONFIG_PRE_CON_BUF_ADDR=0xf0000 CONFIG_LOG_MAX_LEVEL=6 CONFIG_LOG_ERROR_RETURN=y CONFIG_DISPLAY_BOARDINFO_LATE=y +CONFIG_ANDROID_AB=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y CONFIG_CMD_BOOTZ=y @@ -47,6 +48,7 @@ CONFIG_CMD_REMOTEPROC=y CONFIG_CMD_SPI=y CONFIG_CMD_USB=y CONFIG_CMD_AXI=y +CONFIG_CMD_AB_SELECT=y CONFIG_CMD_TFTPPUT=y CONFIG_CMD_TFTPSRV=y CONFIG_CMD_RARP=y diff --git a/test/py/tests/test_ab.py b/test/py/tests/test_ab.py new file mode 100644 index 0000000000..b90ca87a02 --- /dev/null +++ b/test/py/tests/test_ab.py @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: GPL-2.0 +# (C) Copyright 2018 Texas Instruments, <www.ti.com>
+# Test A/B update commands.
+import os +import pytest +import u_boot_utils
+class ABTestDiskImage(object):
- """Disk Image used by the A/B tests."""
- def __init__(self, u_boot_console):
"""Initialize a new ABTestDiskImage object.
Args:
u_boot_console: A U-Boot console.
Returns:
Nothing.
"""
filename = 'test_ab_disk_image.bin'
persistent = u_boot_console.config.persistent_data_dir + '/' + filename
self.path = u_boot_console.config.result_dir + '/' + filename
with u_boot_utils.persistent_file_helper(u_boot_console.log, persistent):
if os.path.exists(persistent):
u_boot_console.log.action('Disk image file ' + persistent +
' already exists')
else:
u_boot_console.log.action('Generating ' + persistent)
fd = os.open(persistent, os.O_RDWR | os.O_CREAT)
os.ftruncate(fd, 524288)
os.close(fd)
cmd = ('sgdisk', persistent)
u_boot_utils.run_and_log(u_boot_console, cmd)
cmd = ('sgdisk', '--new=1:64:512', '-c 1:misc', persistent)
u_boot_utils.run_and_log(u_boot_console, cmd)
cmd = ('sgdisk', '-l', persistent)
u_boot_utils.run_and_log(u_boot_console, cmd)
cmd = ('cp', persistent, self.path)
u_boot_utils.run_and_log(u_boot_console, cmd)
+di = None +@pytest.fixture(scope='function') +def ab_disk_image(u_boot_console):
- global di
- if not di:
di = ABTestDiskImage(u_boot_console)
- return di
+@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('android_ab') +@pytest.mark.buildconfigspec('cmd_ab_select') +@pytest.mark.requiredtool('sgdisk') +def test_ab(ab_disk_image, u_boot_console):
- """Test the 'ab_select' command."""
- u_boot_console.run_command('host bind 0 ' + ab_disk_image.path)
- output = u_boot_console.run_command('ab_select slot_name host 0#misc')
- assert 're-initializing A/B metadata' in output
- assert 'Attempting slot a, tries remaining 7' in output
- output = u_boot_console.run_command('printenv slot_name')
- assert 'slot_name=a' in output
- output = u_boot_console.run_command('ab_select slot_name host 0:1')
- assert 'Attempting slot b, tries remaining 7' in output
- output = u_boot_console.run_command('printenv slot_name')
- assert 'slot_name=b' in output
-- 2.17.1

From: Ruslan Trofymenko ruslan.trofymenko@linaro.org
Add a short documentation for A/B enablement and 'ab_select' command usage.
Signed-off-by: Ruslan Trofymenko ruslan.trofymenko@linaro.org Signed-off-by: Igor Opaniuk igor.opaniuk@gmail.com Reviewed-by: Alistair Strachan astrachan@google.com Reviewed-by: Sam Protsenko semen.protsenko@linaro.org Reviewed-by: Simon Glass sjg@chromium.org --- Changes in v4: None Changes in v3: None Changes in v2: * Changes related to command renaming
doc/README.android-ab | 67 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 doc/README.android-ab
diff --git a/doc/README.android-ab b/doc/README.android-ab new file mode 100644 index 0000000000..9f37ed5c58 --- /dev/null +++ b/doc/README.android-ab @@ -0,0 +1,67 @@ +Android A/B updates +=================== + +Overview +-------- + +A/B system updates ensures modern approach for system update. This feature +allows one to use two sets (or more) of partitions referred to as slots +(normally slot A and slot B). The system runs from the current slot while the +partitions in the unused slot can be updated [1]. + +A/B enablement +-------------- + +The A/B updates support can be activated by specifying next options in +your board configuration file: + + CONFIG_ANDROID_AB=y + CONFIG_CMD_AB_SELECT=y + +The disk space on target device must be partitioned in a way so that each +partition which needs to be updated has two or more instances. The name of +each instance must be formed by adding suffixes: _a, _b, _c, etc. +For example: boot_a, boot_b, system_a, system_b, vendor_a, vendor_b. + +As a result you can use 'ab_select' command to ensure A/B boot process in your +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. + +Command usage +------------- + + ab_select <slot_var_name> <interface> <dev[:part_number|#part_name]> + +for example: + + => ab_select slot_name mmc 1:4 + +or + + => ab_select slot_name mmc 1#misc + +Result: + + => printenv slot_name + slot_name=a + +Based on this slot information, the current boot partition should be defined, +and next kernel command line parameters should be generated: + + - androidboot.slot_suffix= + - root= + +For example: + + androidboot.slot_suffix=_a root=/dev/mmcblk1p12 + +A/B metadata is organized according to AOSP reference [2]. On the first system +start with A/B enabled, when 'misc' partition doesn't contain required data, +the default A/B metadata will be created and written to 'misc' partition. + +References +---------- + +[1] https://source.android.com/devices/tech/ota/ab +[2] bootable/recovery/bootloader_message/include/bootloader_message/bootloader_message.h

Hi Igor,
On Tue, Jul 2, 2019 at 11:51 AM Igor Opaniuk igor.opaniuk@gmail.com wrote:
From: Ruslan Trofymenko ruslan.trofymenko@linaro.org
Add a short documentation for A/B enablement and 'ab_select' command usage.
Signed-off-by: Ruslan Trofymenko ruslan.trofymenko@linaro.org Signed-off-by: Igor Opaniuk igor.opaniuk@gmail.com Reviewed-by: Alistair Strachan astrachan@google.com Reviewed-by: Sam Protsenko semen.protsenko@linaro.org Reviewed-by: Simon Glass sjg@chromium.org
Changes in v4: None Changes in v3: None Changes in v2:
- Changes related to command renaming
doc/README.android-ab | 67 +++++++++++++++++++++++++++++++++++++++++++
It was decided to keep Android related documentation in doc/android/. Can you please move it like this:
doc/README.android-ab -> doc/android/ab.txt
Exactly this format should be used, as Eugeniu already sent the patch with such a naming style.
1 file changed, 67 insertions(+) create mode 100644 doc/README.android-ab
diff --git a/doc/README.android-ab b/doc/README.android-ab new file mode 100644 index 0000000000..9f37ed5c58 --- /dev/null +++ b/doc/README.android-ab @@ -0,0 +1,67 @@ +Android A/B updates +===================
+Overview +--------
+A/B system updates ensures modern approach for system update. This feature +allows one to use two sets (or more) of partitions referred to as slots +(normally slot A and slot B). The system runs from the current slot while the +partitions in the unused slot can be updated [1].
+A/B enablement +--------------
+The A/B updates support can be activated by specifying next options in +your board configuration file:
- CONFIG_ANDROID_AB=y
- CONFIG_CMD_AB_SELECT=y
+The disk space on target device must be partitioned in a way so that each +partition which needs to be updated has two or more instances. The name of +each instance must be formed by adding suffixes: _a, _b, _c, etc. +For example: boot_a, boot_b, system_a, system_b, vendor_a, vendor_b.
+As a result you can use 'ab_select' command to ensure A/B boot process in your +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.
+Command usage +-------------
- ab_select <slot_var_name> <interface> <dev[:part_number|#part_name]>
+for example:
- => ab_select slot_name mmc 1:4
+or
- => ab_select slot_name mmc 1#misc
+Result:
- => printenv slot_name
- slot_name=a
+Based on this slot information, the current boot partition should be defined, +and next kernel command line parameters should be generated:
- androidboot.slot_suffix=
- root=
+For example:
- androidboot.slot_suffix=_a root=/dev/mmcblk1p12
+A/B metadata is organized according to AOSP reference [2]. On the first system +start with A/B enabled, when 'misc' partition doesn't contain required data, +the default A/B metadata will be created and written to 'misc' partition.
+References +----------
+[1] https://source.android.com/devices/tech/ota/ab
+[2] bootable/recovery/bootloader_message/include/bootloader_message/bootloader_message.h
2.17.1

From: Ruslan Trofymenko ruslan.trofymenko@linaro.org
Add support for A/B boot process on AM57xx based boards:
1. Define 'slot_suffix' variable (using 'ab_select' command) 2. Extend 'emmc_android_boot' boot command (add commands for A/B boot process)
'ab_select' command is used to decide which slot should be used for booting up. A/B metadata resides in 'misc' partition.
To activate the A/B boot process, the following config options must be set:
CONFIG_ANDROID_AB=y CONFIG_CMD_AB_SELECT=y
For successful A/B boot, the corresponding A/B infrastructure must be involved on Android side [1] (including mounting system as root), and disk must be partitioned accordingly.
When A/B boot is enabled, there are some known limitations currently exist (not related to A/B patches, need to be implemented later):
1. The 'Verified Boot' sequence is not supported 2. dev path to system partition (system_a or system_b) is passed via 'bootargs' as 'root=' argument like 'root=/dev/mmcblk1p12', but further we'll need to rework it with respect to dm-verity requirements [2]
In case when A/B partitions are not present in system (and A/B boot is enabled), boot up process will be terminated and next message will be shown:
"boot_a(b) partition not found"
[1] https://source.android.com/devices/tech/ota/ab [2] https://source.android.com/devices/tech/ota/ab/ab_implement#kernel
Signed-off-by: Ruslan Trofymenko ruslan.trofymenko@linaro.org Signed-off-by: Igor Opaniuk igor.opaniuk@gmail.com Reviewed-by: Alistair Strachan astrachan@google.com Reviewed-by: Sam Protsenko semen.protsenko@linaro.org Reviewed-by: Simon Glass sjg@chromium.org
---
Changes in v4: None Changes in v3: None
Changes in v2: * Add changes related to command renaming (android_ab_select -> ab_select). * Slotted sections (e.g. system_a and system_b) are added to the default sections if CONFIG_CMD_AB_SELECT flag is defined * Rebased on top of master * system partitions sizes increased to 1024 MiB (to be consistent with recent changes to boot.h file)
include/environment/ti/boot.h | 58 +++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 6 deletions(-)
diff --git a/include/environment/ti/boot.h b/include/environment/ti/boot.h index 943adcad40..e340164245 100644 --- a/include/environment/ti/boot.h +++ b/include/environment/ti/boot.h @@ -23,6 +23,18 @@ #define VBMETA_PART "" #endif
+#if defined(CONFIG_CMD_AB_SELECT) +#define COMMON_PARTS \ + "name=boot_a,size=10M,uuid=${uuid_gpt_boot_a};" \ + "name=boot_b,size=10M,uuid=${uuid_gpt_boot_b};" \ + "name=system_a,size=1024M,uuid=${uuid_gpt_system_a};" \ + "name=system_b,size=1024M,uuid=${uuid_gpt_system_b};" +#else +#define COMMON_PARTS \ + "name=boot,size=10M,uuid=${uuid_gpt_boot};" \ + "name=system,size=1024M,uuid=${uuid_gpt_system};" +#endif + #ifndef PARTS_DEFAULT /* Define the default GPT table for eMMC */ #define PARTS_DEFAULT \ @@ -38,8 +50,7 @@ "name=uboot-env,start=2432K,size=256K,uuid=${uuid_gpt_reserved};" \ "name=misc,size=128K,uuid=${uuid_gpt_misc};" \ "name=recovery,size=40M,uuid=${uuid_gpt_recovery};" \ - "name=boot,size=20M,uuid=${uuid_gpt_boot};" \ - "name=system,size=1024M,uuid=${uuid_gpt_system};" \ + COMMON_PARTS \ "name=vendor,size=256M,uuid=${uuid_gpt_vendor};" \ VBMETA_PART \ "name=userdata,size=-,uuid=${uuid_gpt_userdata}" @@ -58,6 +69,35 @@ #define AVB_VERIFY_CMD "" #endif
+#define CONTROL_PARTITION "misc" + +#if defined(CONFIG_CMD_AB_SELECT) +#define AB_SELECT \ + "if part number mmc 1 " CONTROL_PARTITION " control_part_number; " \ + "then " \ + "echo " CONTROL_PARTITION \ + " partition number:${control_part_number};" \ + "ab_select slot_name mmc ${mmcdev}:${control_part_number};" \ + "else " \ + "echo " CONTROL_PARTITION " partition not found;" \ + "exit;" \ + "fi;" \ + "setenv slot_suffix _${slot_name};" \ + "if part number mmc ${mmcdev} system${slot_suffix} " \ + "system_part_number; then " \ + "setenv bootargs_ab " \ + "ro root=/dev/mmcblk${mmcdev}p${system_part_number} " \ + "rootwait init=/init skip_initramfs " \ + "androidboot.slot_suffix=${slot_suffix};" \ + "echo A/B cmdline addition: ${bootargs_ab};" \ + "setenv bootargs ${bootargs} ${bootargs_ab};" \ + "else " \ + "echo system${slot_suffix} partition not found;" \ + "fi;" +#else +#define AB_SELECT "" +#endif + #define DEFAULT_COMMON_BOOT_TI_ARGS \ "console=" CONSOLEDEV ",115200n8\0" \ "fdtfile=undefined\0" \ @@ -86,10 +126,16 @@ "mmc dev $mmcdev; " \ "mmc rescan; " \ AVB_VERIFY_CHECK \ - "part start mmc ${mmcdev} boot boot_start; " \ - "part size mmc ${mmcdev} boot boot_size; " \ - "mmc read ${loadaddr} ${boot_start} ${boot_size}; " \ - "bootm ${loadaddr}#${fdtfile};\0 " + AB_SELECT \ + "if part start mmc ${mmcdev} boot${slot_suffix} boot_start; " \ + "then " \ + "part size mmc ${mmcdev} boot${slot_suffix} " \ + "boot_size; " \ + "mmc read ${loadaddr} ${boot_start} ${boot_size}; " \ + "bootm ${loadaddr}#${fdtfile}; " \ + "else " \ + "echo boot${slot_suffix} partition not found; " \ + "fi;\0"
#ifdef CONFIG_OMAP54XX

Hi Igor,
On Tue, Jul 2, 2019 at 11:51 AM Igor Opaniuk igor.opaniuk@gmail.com wrote:
From: Ruslan Trofymenko ruslan.trofymenko@linaro.org
Add support for A/B boot process on AM57xx based boards:
- Define 'slot_suffix' variable (using 'ab_select' command)
- Extend 'emmc_android_boot' boot command (add commands for A/B boot process)
'ab_select' command is used to decide which slot should be used for booting up. A/B metadata resides in 'misc' partition.
To activate the A/B boot process, the following config options must be set:
CONFIG_ANDROID_AB=y CONFIG_CMD_AB_SELECT=y
For successful A/B boot, the corresponding A/B infrastructure must be involved on Android side [1] (including mounting system as root), and disk must be partitioned accordingly.
When A/B boot is enabled, there are some known limitations currently exist (not related to A/B patches, need to be implemented later):
- The 'Verified Boot' sequence is not supported
- dev path to system partition (system_a or system_b) is passed via 'bootargs' as 'root=' argument like 'root=/dev/mmcblk1p12', but further we'll need to rework it with respect to dm-verity requirements [2]
In case when A/B partitions are not present in system (and A/B boot is enabled), boot up process will be terminated and next message will be shown:
"boot_a(b) partition not found"
[1] https://source.android.com/devices/tech/ota/ab [2] https://source.android.com/devices/tech/ota/ab/ab_implement#kernel
Signed-off-by: Ruslan Trofymenko ruslan.trofymenko@linaro.org Signed-off-by: Igor Opaniuk igor.opaniuk@gmail.com Reviewed-by: Alistair Strachan astrachan@google.com Reviewed-by: Sam Protsenko semen.protsenko@linaro.org Reviewed-by: Simon Glass sjg@chromium.org
Changes in v4: None Changes in v3: None
Changes in v2:
- Add changes related to command renaming (android_ab_select -> ab_select).
- Slotted sections (e.g. system_a and system_b) are added to the default sections if CONFIG_CMD_AB_SELECT flag is defined
- Rebased on top of master
- system partitions sizes increased to 1024 MiB (to be consistent with recent changes to boot.h file)
include/environment/ti/boot.h | 58 +++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 6 deletions(-)
diff --git a/include/environment/ti/boot.h b/include/environment/ti/boot.h index 943adcad40..e340164245 100644 --- a/include/environment/ti/boot.h +++ b/include/environment/ti/boot.h @@ -23,6 +23,18 @@ #define VBMETA_PART "" #endif
+#if defined(CONFIG_CMD_AB_SELECT) +#define COMMON_PARTS \
"name=boot_a,size=10M,uuid=${uuid_gpt_boot_a};" \
"name=boot_b,size=10M,uuid=${uuid_gpt_boot_b};" \
I guess this patch will be applied on top of this one:
https://patchwork.ozlabs.org/patch/1126434/
So can you please increase size= param for boot_a and boot_b from 10M to 20M?
"name=system_a,size=1024M,uuid=${uuid_gpt_system_a};" \
"name=system_b,size=1024M,uuid=${uuid_gpt_system_b};"
+#else +#define COMMON_PARTS \
"name=boot,size=10M,uuid=${uuid_gpt_boot};" \
The same here, should be 20M.
"name=system,size=1024M,uuid=${uuid_gpt_system};"
+#endif
#ifndef PARTS_DEFAULT /* Define the default GPT table for eMMC */ #define PARTS_DEFAULT \ @@ -38,8 +50,7 @@ "name=uboot-env,start=2432K,size=256K,uuid=${uuid_gpt_reserved};" \ "name=misc,size=128K,uuid=${uuid_gpt_misc};" \ "name=recovery,size=40M,uuid=${uuid_gpt_recovery};" \
"name=boot,size=20M,uuid=${uuid_gpt_boot};" \
"name=system,size=1024M,uuid=${uuid_gpt_system};" \
COMMON_PARTS \ "name=vendor,size=256M,uuid=${uuid_gpt_vendor};" \ VBMETA_PART \ "name=userdata,size=-,uuid=${uuid_gpt_userdata}"
@@ -58,6 +69,35 @@ #define AVB_VERIFY_CMD "" #endif
+#define CONTROL_PARTITION "misc"
+#if defined(CONFIG_CMD_AB_SELECT) +#define AB_SELECT \
"if part number mmc 1 " CONTROL_PARTITION " control_part_number; " \
"then " \
"echo " CONTROL_PARTITION \
" partition number:${control_part_number};" \
"ab_select slot_name mmc ${mmcdev}:${control_part_number};" \
"else " \
"echo " CONTROL_PARTITION " partition not found;" \
"exit;" \
"fi;" \
"setenv slot_suffix _${slot_name};" \
"if part number mmc ${mmcdev} system${slot_suffix} " \
"system_part_number; then " \
"setenv bootargs_ab " \
"ro root=/dev/mmcblk${mmcdev}p${system_part_number} " \
"rootwait init=/init skip_initramfs " \
"androidboot.slot_suffix=${slot_suffix};" \
"echo A/B cmdline addition: ${bootargs_ab};" \
"setenv bootargs ${bootargs} ${bootargs_ab};" \
"else " \
"echo system${slot_suffix} partition not found;" \
"fi;"
+#else +#define AB_SELECT "" +#endif
#define DEFAULT_COMMON_BOOT_TI_ARGS \ "console=" CONSOLEDEV ",115200n8\0" \ "fdtfile=undefined\0" \ @@ -86,10 +126,16 @@ "mmc dev $mmcdev; " \ "mmc rescan; " \ AVB_VERIFY_CHECK \
"part start mmc ${mmcdev} boot boot_start; " \
"part size mmc ${mmcdev} boot boot_size; " \
"mmc read ${loadaddr} ${boot_start} ${boot_size}; " \
"bootm ${loadaddr}#${fdtfile};\0 "
AB_SELECT \
"if part start mmc ${mmcdev} boot${slot_suffix} boot_start; " \
"then " \
"part size mmc ${mmcdev} boot${slot_suffix} " \
"boot_size; " \
"mmc read ${loadaddr} ${boot_start} ${boot_size}; " \
"bootm ${loadaddr}#${fdtfile}; " \
"else " \
"echo boot${slot_suffix} partition not found; " \
"fi;\0"
#ifdef CONFIG_OMAP54XX
-- 2.17.1
participants (2)
-
Igor Opaniuk
-
Sam Protsenko