* [U-Boot] [PATCH v2 0/7] android: Implement A/B boot process
@ 2018-12-12 17:12 Ruslan Trofymenko
2018-12-12 17:12 ` [U-Boot] [PATCH v2 1/7] cmd: part: Add 'number' sub-command Ruslan Trofymenko
` (7 more replies)
0 siblings, 8 replies; 18+ messages in thread
From: Ruslan Trofymenko @ 2018-12-12 17:12 UTC (permalink / raw)
To: u-boot
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 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
Ruslan Trofymenko (7):
cmd: part: Add 'number' sub-command
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 ++++++++
cmd/part.c | 16 ++-
common/Kconfig | 10 ++
common/Makefile | 1 +
common/android_ab.c | 277 ++++++++++++++++++++++++++++++++++++++++++
configs/sandbox_defconfig | 2 +
disk/part.c | 68 +++++++++++
doc/README.android-ab | 67 ++++++++++
include/android_ab.h | 34 ++++++
include/android_bl_msg.h | 169 ++++++++++++++++++++++++++
include/environment/ti/boot.h | 58 ++++++++-
include/part.h | 21 ++++
test/py/tests/test_ab.py | 74 +++++++++++
15 files changed, 858 insertions(+), 7 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 include/android_bl_msg.h
create mode 100644 test/py/tests/test_ab.py
--
2.7.4
^ permalink raw reply [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 1/7] cmd: part: Add 'number' sub-command
2018-12-12 17:12 [U-Boot] [PATCH v2 0/7] android: Implement A/B boot process Ruslan Trofymenko
@ 2018-12-12 17:12 ` Ruslan Trofymenko
2019-01-15 10:04 ` Igor Opaniuk
2018-12-12 17:12 ` [U-Boot] [PATCH v2 2/7] disk: part: Extend API to get partition info Ruslan Trofymenko
` (6 subsequent siblings)
7 siblings, 1 reply; 18+ messages in thread
From: Ruslan Trofymenko @ 2018-12-12 17:12 UTC (permalink / raw)
To: u-boot
This sub-command serves for getting the partition index from
partition name. Also it can be used to test the existence of specified
partition.
Signed-off-by: Ruslan Trofymenko <ruslan.trofymenko@linaro.org>
Reviewed-by: Alistair Strachan <astrachan@google.com>
Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
---
Changes in v2: None
cmd/part.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/cmd/part.c b/cmd/part.c
index bfb6488..653e13c 100644
--- a/cmd/part.c
+++ b/cmd/part.c
@@ -24,6 +24,7 @@
enum cmd_part_info {
CMD_PART_INFO_START = 0,
CMD_PART_INFO_SIZE,
+ CMD_PART_INFO_NUMBER
};
static int do_part_uuid(int argc, char * const argv[])
@@ -149,6 +150,9 @@ static int do_part_info(int argc, char * const argv[], enum cmd_part_info param)
case CMD_PART_INFO_SIZE:
snprintf(buf, sizeof(buf), LBAF, info.size);
break;
+ case CMD_PART_INFO_NUMBER:
+ snprintf(buf, sizeof(buf), "%d", part);
+ break;
default:
printf("** Unknown cmd_part_info value: %d\n", param);
return 1;
@@ -172,6 +176,11 @@ static int do_part_size(int argc, char * const argv[])
return do_part_info(argc, argv, CMD_PART_INFO_SIZE);
}
+static int do_part_number(int argc, char * const argv[])
+{
+ return do_part_info(argc, argv, CMD_PART_INFO_NUMBER);
+}
+
static int do_part(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
if (argc < 2)
@@ -185,6 +194,8 @@ static int do_part(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
return do_part_start(argc - 2, argv + 2);
else if (!strcmp(argv[1], "size"))
return do_part_size(argc - 2, argv + 2);
+ else if (!strcmp(argv[1], "number"))
+ return do_part_number(argc - 2, argv + 2);
return CMD_RET_USAGE;
}
@@ -206,5 +217,8 @@ U_BOOT_CMD(
" part can be either partition number or partition name\n"
"part size <interface> <dev> <part> <varname>\n"
" - set environment variable to the size of the partition (in blocks)\n"
- " part can be either partition number or partition name"
+ " part can be either partition number or partition name\n"
+ "part number <interface> <dev> <part> <varname>\n"
+ " - set environment variable to the partition number using the partition name\n"
+ " part must be specified as partition name"
);
--
2.7.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 2/7] disk: part: Extend API to get partition info
2018-12-12 17:12 [U-Boot] [PATCH v2 0/7] android: Implement A/B boot process Ruslan Trofymenko
2018-12-12 17:12 ` [U-Boot] [PATCH v2 1/7] cmd: part: Add 'number' sub-command Ruslan Trofymenko
@ 2018-12-12 17:12 ` Ruslan Trofymenko
2019-01-05 1:56 ` Simon Glass
2018-12-12 17:12 ` [U-Boot] [PATCH v2 3/7] common: Implement A/B metadata Ruslan Trofymenko
` (5 subsequent siblings)
7 siblings, 1 reply; 18+ messages in thread
From: Ruslan Trofymenko @ 2018-12-12 17:12 UTC (permalink / raw)
To: u-boot
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/2
Signed-off-by: Ruslan Trofymenko <ruslan.trofymenko@linaro.org>
Reviewed-by: Alistair Strachan <astrachan@google.com>
Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
---
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 f30f9e9..7b739ad 100644
--- a/disk/part.c
+++ b/disk/part.c
@@ -675,6 +675,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 0750aee..9d57b65 100644
--- a/include/part.h
+++ b/include/part.h
@@ -202,6 +202,27 @@ 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
*
* Helper function for partition tables, which don't hold partition names
--
2.7.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 3/7] common: Implement A/B metadata
2018-12-12 17:12 [U-Boot] [PATCH v2 0/7] android: Implement A/B boot process Ruslan Trofymenko
2018-12-12 17:12 ` [U-Boot] [PATCH v2 1/7] cmd: part: Add 'number' sub-command Ruslan Trofymenko
2018-12-12 17:12 ` [U-Boot] [PATCH v2 2/7] disk: part: Extend API to get partition info Ruslan Trofymenko
@ 2018-12-12 17:12 ` Ruslan Trofymenko
2019-01-15 11:38 ` Igor Opaniuk
2018-12-12 17:12 ` [U-Boot] [PATCH v2 4/7] cmd: Add 'ab_select' command Ruslan Trofymenko
` (4 subsequent siblings)
7 siblings, 1 reply; 18+ messages in thread
From: Ruslan Trofymenko @ 2018-12-12 17:12 UTC (permalink / raw)
To: u-boot
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
[2] https://android-review.googlesource.com/c/platform/external/u-boot/+/729880/2
Signed-off-by: Ruslan Trofymenko <ruslan.trofymenko@linaro.org>
Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
---
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 | 277 +++++++++++++++++++++++++++++++++++++++++++++++
include/android_ab.h | 34 ++++++
include/android_bl_msg.h | 169 +++++++++++++++++++++++++++++
5 files changed, 491 insertions(+)
create mode 100644 common/android_ab.c
create mode 100644 include/android_ab.h
create mode 100644 include/android_bl_msg.h
diff --git a/common/Kconfig b/common/Kconfig
index 57bd16d..0ff4679 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -748,6 +748,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 65d89dc..acc3851 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -104,6 +104,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 0000000..dda2263
--- /dev/null
+++ b/common/android_ab.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ */
+
+#include <common.h>
+#include <android_ab.h>
+#include <android_bl_msg.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.
+ */
+static uint32_t ab_control_compute_crc(struct andr_bl_control *abc)
+{
+ return crc32(0, (void *)abc, offsetof(typeof(*abc), crc32_le));
+}
+
+/**
+ * Initialize andr_bl_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.
+ */
+static void ab_control_default(struct andr_bl_control *abc)
+{
+ int i;
+ const struct andr_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 = ab_control_compute_crc(abc);
+}
+
+/**
+ * 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 andr_bl_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 andr_bl_control **abc)
+{
+ ulong abc_offset, abc_blocks;
+
+ abc_offset = offsetof(struct andr_bl_msg_ab, slot_suffix);
+ if (abc_offset % part_info->blksz) {
+ printf("ANDROID: Boot control block not block aligned.\n");
+ return -EINVAL;
+ }
+ abc_offset /= part_info->blksz;
+
+ abc_blocks = DIV_ROUND_UP(sizeof(struct andr_bl_control),
+ part_info->blksz);
+ if (abc_offset + abc_blocks > part_info->size) {
+ printf("ANDROID: boot control partition too small. Need at");
+ printf(" 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) {
+ printf("ANDROID: Could not read from boot control 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 andr_bl_control *abc)
+{
+ ulong abc_offset, abc_blocks;
+
+ abc_offset = offsetof(struct andr_bl_msg_ab, slot_suffix) /
+ part_info->blksz;
+ abc_blocks = DIV_ROUND_UP(sizeof(struct andr_bl_control),
+ part_info->blksz);
+ if (blk_dwrite(dev_desc, part_info->start + abc_offset, abc_blocks,
+ abc) != abc_blocks) {
+ printf("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 andr_slot_metadata *a,
+ const struct andr_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 andr_bl_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 (!abc || 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) {
+ printf("ANDROID: Invalid CRC-32 (expected %.8x, found %.8x), ",
+ crc32_le, abc->crc32_le);
+ printf("re-initializing A/B metadata.\n");
+ ab_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 -ENODATA;
+ }
+
+ if (abc->version > ANDROID_BOOT_CTRL_VERSION) {
+ printf("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) {
+ 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 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 = 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 0000000..c1b901d
--- /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 ANDROID_BOOT_SLOT_NAME(slot_num) ('a' + (slot_num))
+
+/* Number of slots */
+#define ANDROID_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 */
diff --git a/include/android_bl_msg.h b/include/android_bl_msg.h
new file mode 100644
index 0000000..f37e01a
--- /dev/null
+++ b/include/android_bl_msg.h
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * This file was taken from the AOSP Project.
+ * Repository: https://android.googlesource.com/platform/bootable/recovery/
+ * File: bootloader_message/include/bootloader_message/bootloader_message.h
+ * Commit: 8b309f6970ab3b7c53cc529c51a2cb44e1c7a7e1
+ *
+ * Copyright (C) 2008 The Android Open Source Project
+ */
+
+#ifndef __ANDROID_BL_MSG_H
+#define __ANDROID_BL_MSG_H
+
+/*
+ * compiler.h defines the types that otherwise are included from stdint.h and
+ * stddef.h
+ */
+#include <compiler.h>
+#include <linux/sizes.h>
+
+/*
+ * Spaces used by misc partition are as below:
+ * 0 - 2K Bootloader Message
+ * 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally
+ * used as bootloader_message_ab struct)
+ * 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B
+ * devices
+ * Note that these offsets are admitted by bootloader, recovery and uncrypt, so
+ * they are not configurable without changing all of them.
+ */
+#define ANDROID_MISC_BM_OFFSET 0
+#define ANDROID_MISC_WIPE_OFFSET SZ_16K
+
+/**
+ * Bootloader Message (2-KiB).
+ *
+ * This structure describes the content of a block in flash
+ * that is used for recovery and the bootloader to talk to
+ * each other.
+ *
+ * The command field is updated by linux when it wants to
+ * reboot into recovery or to update radio or bootloader firmware.
+ * It is also updated by the bootloader when firmware update
+ * is complete (to boot into recovery for any final cleanup)
+ *
+ * The status field is written by the bootloader after the
+ * completion of an "update-radio" or "update-hboot" command.
+ *
+ * The recovery field is only written by linux and used
+ * for the system to send a message to recovery or the
+ * other way around.
+ *
+ * The stage field is written by packages which restart themselves
+ * multiple times, so that the UI can reflect which invocation of the
+ * package it is. If the value is of the format "#/#" (eg, "1/3"),
+ * the UI will add a simple indicator of that status.
+ *
+ * We used to have slot_suffix field for A/B boot control metadata in
+ * this struct, which gets unintentionally cleared by recovery or
+ * uncrypt. Move it into struct bootloader_message_ab to avoid the
+ * issue.
+ */
+struct andr_bl_msg {
+ char command[32];
+ char status[32];
+ char recovery[768];
+
+ /*
+ * The 'recovery' field used to be 1024 bytes. It has only ever
+ * been used to store the recovery command line, so 768 bytes
+ * should be plenty. We carve off the last 256 bytes to store the
+ * stage string (for multistage packages) and possible future
+ * expansion.
+ */
+ char stage[32];
+
+ /*
+ * The 'reserved' field used to be 224 bytes when it was initially
+ * carved off from the 1024-byte recovery field. Bump it up to
+ * 1184-byte so that the entire bootloader_message struct rounds up
+ * to 2048-byte.
+ */
+ char reserved[1184];
+};
+
+/**
+ * The A/B-specific bootloader message structure (4-KiB).
+ *
+ * We separate A/B boot control metadata from the regular bootloader
+ * message struct and keep it here. Everything that's A/B-specific
+ * stays after struct bootloader_message, which should be managed by
+ * the A/B-bootloader or boot control HAL.
+ *
+ * The slot_suffix field is used for A/B implementations where the
+ * bootloader does not set the androidboot.ro.boot.slot_suffix kernel
+ * commandline parameter. This is used by fs_mgr to mount /system and
+ * other partitions with the slotselect flag set in fstab. A/B
+ * implementations are free to use all 32 bytes and may store private
+ * data past the first NUL-byte in this field. It is encouraged, but
+ * not mandatory, to use 'struct bootloader_control' described below.
+ *
+ * The update_channel field is used to store the Omaha update channel
+ * if update_engine is compiled with Omaha support.
+ */
+struct andr_bl_msg_ab {
+ struct andr_bl_msg message;
+ char slot_suffix[32];
+ char update_channel[128];
+
+ /* Round up the entire struct to 4096-byte */
+ char reserved[1888];
+};
+
+#define ANDROID_BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */
+#define ANDROID_BOOT_CTRL_VERSION 1
+
+struct andr_slot_metadata {
+ /*
+ * Slot priority with 15 meaning highest priority, 1 lowest
+ * priority and 0 the slot is unbootable
+ */
+ u8 priority : 4;
+ /* Number of times left attempting to boot this slot */
+ u8 tries_remaining : 3;
+ /* 1 if this slot has booted successfully, 0 otherwise */
+ u8 successful_boot : 1;
+ /*
+ * 1 if this slot is corrupted from a dm-verity corruption,
+ * 0 otherwise
+ */
+ u8 verity_corrupted : 1;
+ /* Reserved for further use */
+ u8 reserved : 7;
+} __packed;
+
+/**
+ * Bootloader Control AB.
+ *
+ * This struct can be used to manage A/B metadata. It is designed to
+ * be put in the 'slot_suffix' field of the 'bootloader_message'
+ * structure described above. It is encouraged to use the
+ * 'bootloader_control' structure to store the A/B metadata, but not
+ * mandatory.
+ */
+struct andr_bl_control {
+ /* NULL terminated active slot suffix */
+ char slot_suffix[4];
+ /* Bootloader Control AB magic number (see BOOT_CTRL_MAGIC) */
+ u32 magic;
+ /* Version of struct being used (see BOOT_CTRL_VERSION) */
+ u8 version;
+ /* Number of slots being managed */
+ u8 nb_slot : 3;
+ /* Number of times left attempting to boot recovery */
+ u8 recovery_tries_remaining : 3;
+ /* Ensure 4-bytes alignment for slot_info field */
+ u8 reserved0[2];
+ /* Per-slot information. Up to 4 slots */
+ struct andr_slot_metadata slot_info[4];
+ /* Reserved for further use */
+ u8 reserved1[8];
+ /*
+ * CRC32 of all 28 bytes preceding this field (little endian
+ * format)
+ */
+ u32 crc32_le;
+} __packed;
+
+#endif /* __ANDROID_BL_MSG_H */
--
2.7.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 4/7] cmd: Add 'ab_select' command
2018-12-12 17:12 [U-Boot] [PATCH v2 0/7] android: Implement A/B boot process Ruslan Trofymenko
` (2 preceding siblings ...)
2018-12-12 17:12 ` [U-Boot] [PATCH v2 3/7] common: Implement A/B metadata Ruslan Trofymenko
@ 2018-12-12 17:12 ` Ruslan Trofymenko
2019-01-15 10:46 ` Igor Opaniuk
2018-12-12 17:12 ` [U-Boot] [PATCH v2 5/7] test/py: Add base test case for A/B updates Ruslan Trofymenko
` (3 subsequent siblings)
7 siblings, 1 reply; 18+ messages in thread
From: Ruslan Trofymenko @ 2018-12-12 17:12 UTC (permalink / raw)
To: u-boot
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/2
Signed-off-by: Ruslan Trofymenko <ruslan.trofymenko@linaro.org>
Reviewed-by: Alistair Strachan <astrachan@google.com>
Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
---
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 ea1a325..ed60e1e 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1123,6 +1123,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 4998643..1d345c1 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 0000000..2a9e524
--- /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] = 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(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"
+);
--
2.7.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 5/7] test/py: Add base test case for A/B updates
2018-12-12 17:12 [U-Boot] [PATCH v2 0/7] android: Implement A/B boot process Ruslan Trofymenko
` (3 preceding siblings ...)
2018-12-12 17:12 ` [U-Boot] [PATCH v2 4/7] cmd: Add 'ab_select' command Ruslan Trofymenko
@ 2018-12-12 17:12 ` Ruslan Trofymenko
2019-01-05 1:56 ` Simon Glass
2018-12-12 17:12 ` [U-Boot] [PATCH v2 6/7] doc: android: Add simple guide " Ruslan Trofymenko
` (2 subsequent siblings)
7 siblings, 1 reply; 18+ messages in thread
From: Ruslan Trofymenko @ 2018-12-12 17:12 UTC (permalink / raw)
To: u-boot
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>
Reviewed-by: Alistair Strachan <astrachan@google.com>
Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
---
Changes in v2:
* Сhanges related to command renaming
* 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 5b65c61..b6a642e 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -21,6 +21,7 @@ CONFIG_PRE_CON_BUF_ADDR=0x100000
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
@@ -49,6 +50,7 @@ CONFIG_CMD_SF=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 0000000..b90ca87
--- /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
+ at pytest.fixture(scope='function')
+def ab_disk_image(u_boot_console):
+ global di
+ if not di:
+ di = ABTestDiskImage(u_boot_console)
+ return di
+
+ at pytest.mark.boardspec('sandbox')
+ at pytest.mark.buildconfigspec('android_ab')
+ at pytest.mark.buildconfigspec('cmd_ab_select')
+ at 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.7.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 6/7] doc: android: Add simple guide for A/B updates
2018-12-12 17:12 [U-Boot] [PATCH v2 0/7] android: Implement A/B boot process Ruslan Trofymenko
` (4 preceding siblings ...)
2018-12-12 17:12 ` [U-Boot] [PATCH v2 5/7] test/py: Add base test case for A/B updates Ruslan Trofymenko
@ 2018-12-12 17:12 ` Ruslan Trofymenko
2019-01-05 1:56 ` Simon Glass
2019-01-15 10:20 ` Igor Opaniuk
2018-12-12 17:12 ` [U-Boot] [PATCH v2 7/7] env: am57xx: Implement A/B boot process Ruslan Trofymenko
2018-12-17 13:05 ` [U-Boot] [PATCH v2 0/7] android: " Sam Protsenko
7 siblings, 2 replies; 18+ messages in thread
From: Ruslan Trofymenko @ 2018-12-12 17:12 UTC (permalink / raw)
To: u-boot
Add a short documentation for A/B enablement and 'ab_select' command
usage.
Signed-off-by: Ruslan Trofymenko <ruslan.trofymenko@linaro.org>
Reviewed-by: Alistair Strachan <astrachan@google.com>
Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
---
Changes in v2:
* Сhanges related to command renaming (android_ab_select -> ab_select)
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 0000000..9f37ed5
--- /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.7.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 7/7] env: am57xx: Implement A/B boot process
2018-12-12 17:12 [U-Boot] [PATCH v2 0/7] android: Implement A/B boot process Ruslan Trofymenko
` (5 preceding siblings ...)
2018-12-12 17:12 ` [U-Boot] [PATCH v2 6/7] doc: android: Add simple guide " Ruslan Trofymenko
@ 2018-12-12 17:12 ` Ruslan Trofymenko
2019-01-05 1:56 ` Simon Glass
2019-01-15 11:42 ` Igor Opaniuk
2018-12-17 13:05 ` [U-Boot] [PATCH v2 0/7] android: " Sam Protsenko
7 siblings, 2 replies; 18+ messages in thread
From: Ruslan Trofymenko @ 2018-12-12 17:12 UTC (permalink / raw)
To: u-boot
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>
Reviewed-by: Alistair Strachan <astrachan@google.com>
Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
---
Changes in v2:
* Сhanges 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 5891009..e3ba689 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 \
@@ -40,8 +52,7 @@
"name=efs,size=16M,uuid=${uuid_gpt_efs};" \
"name=crypto,size=16K,uuid=${uuid_gpt_crypto};" \
"name=recovery,size=40M,uuid=${uuid_gpt_recovery};" \
- "name=boot,size=10M,uuid=${uuid_gpt_boot};" \
- "name=system,size=768M,uuid=${uuid_gpt_system};" \
+ COMMON_PARTS \
"name=vendor,size=256M,uuid=${uuid_gpt_vendor};" \
"name=cache,size=256M,uuid=${uuid_gpt_cache};" \
"name=ipu1,size=1M,uuid=${uuid_gpt_ipu1};" \
@@ -63,6 +74,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" \
@@ -91,10 +131,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.7.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 0/7] android: Implement A/B boot process
2018-12-12 17:12 [U-Boot] [PATCH v2 0/7] android: Implement A/B boot process Ruslan Trofymenko
` (6 preceding siblings ...)
2018-12-12 17:12 ` [U-Boot] [PATCH v2 7/7] env: am57xx: Implement A/B boot process Ruslan Trofymenko
@ 2018-12-17 13:05 ` Sam Protsenko
7 siblings, 0 replies; 18+ messages in thread
From: Sam Protsenko @ 2018-12-17 13:05 UTC (permalink / raw)
To: u-boot
For the whole series:
Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
On Wed, Dec 12, 2018 at 7:12 PM Ruslan Trofymenko
<ruslan.trofymenko@linaro.org> wrote:
>
> 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 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
>
> Ruslan Trofymenko (7):
> cmd: part: Add 'number' sub-command
> 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 ++++++++
> cmd/part.c | 16 ++-
> common/Kconfig | 10 ++
> common/Makefile | 1 +
> common/android_ab.c | 277 ++++++++++++++++++++++++++++++++++++++++++
> configs/sandbox_defconfig | 2 +
> disk/part.c | 68 +++++++++++
> doc/README.android-ab | 67 ++++++++++
> include/android_ab.h | 34 ++++++
> include/android_bl_msg.h | 169 ++++++++++++++++++++++++++
> include/environment/ti/boot.h | 58 ++++++++-
> include/part.h | 21 ++++
> test/py/tests/test_ab.py | 74 +++++++++++
> 15 files changed, 858 insertions(+), 7 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 include/android_bl_msg.h
> create mode 100644 test/py/tests/test_ab.py
>
> --
> 2.7.4
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 2/7] disk: part: Extend API to get partition info
2018-12-12 17:12 ` [U-Boot] [PATCH v2 2/7] disk: part: Extend API to get partition info Ruslan Trofymenko
@ 2019-01-05 1:56 ` Simon Glass
0 siblings, 0 replies; 18+ messages in thread
From: Simon Glass @ 2019-01-05 1:56 UTC (permalink / raw)
To: u-boot
On Wed, 12 Dec 2018 at 10:12, Ruslan Trofymenko
<ruslan.trofymenko@linaro.org> wrote:
>
> 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/2
>
> Signed-off-by: Ruslan Trofymenko <ruslan.trofymenko@linaro.org>
> Reviewed-by: Alistair Strachan <astrachan@google.com>
> Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
> ---
> 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(+)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 5/7] test/py: Add base test case for A/B updates
2018-12-12 17:12 ` [U-Boot] [PATCH v2 5/7] test/py: Add base test case for A/B updates Ruslan Trofymenko
@ 2019-01-05 1:56 ` Simon Glass
0 siblings, 0 replies; 18+ messages in thread
From: Simon Glass @ 2019-01-05 1:56 UTC (permalink / raw)
To: u-boot
On Wed, 12 Dec 2018 at 10:12, Ruslan Trofymenko
<ruslan.trofymenko@linaro.org> wrote:
>
> 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>
> Reviewed-by: Alistair Strachan <astrachan@google.com>
> Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
> ---
> Changes in v2:
> * Сhanges related to command renaming
> * 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
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 6/7] doc: android: Add simple guide for A/B updates
2018-12-12 17:12 ` [U-Boot] [PATCH v2 6/7] doc: android: Add simple guide " Ruslan Trofymenko
@ 2019-01-05 1:56 ` Simon Glass
2019-01-15 10:20 ` Igor Opaniuk
1 sibling, 0 replies; 18+ messages in thread
From: Simon Glass @ 2019-01-05 1:56 UTC (permalink / raw)
To: u-boot
On Wed, 12 Dec 2018 at 10:12, Ruslan Trofymenko
<ruslan.trofymenko@linaro.org> wrote:
>
> Add a short documentation for A/B enablement and 'ab_select' command
> usage.
>
> Signed-off-by: Ruslan Trofymenko <ruslan.trofymenko@linaro.org>
> Reviewed-by: Alistair Strachan <astrachan@google.com>
> Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
> ---
> Changes in v2:
> * Сhanges related to command renaming (android_ab_select -> ab_select)
>
> doc/README.android-ab | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 67 insertions(+)
> create mode 100644 doc/README.android-ab
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 7/7] env: am57xx: Implement A/B boot process
2018-12-12 17:12 ` [U-Boot] [PATCH v2 7/7] env: am57xx: Implement A/B boot process Ruslan Trofymenko
@ 2019-01-05 1:56 ` Simon Glass
2019-01-15 11:42 ` Igor Opaniuk
1 sibling, 0 replies; 18+ messages in thread
From: Simon Glass @ 2019-01-05 1:56 UTC (permalink / raw)
To: u-boot
On Wed, 12 Dec 2018 at 10:12, Ruslan Trofymenko
<ruslan.trofymenko@linaro.org> wrote:
>
> 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>
> Reviewed-by: Alistair Strachan <astrachan@google.com>
> Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
> ---
> Changes in v2:
> * Сhanges 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(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 1/7] cmd: part: Add 'number' sub-command
2018-12-12 17:12 ` [U-Boot] [PATCH v2 1/7] cmd: part: Add 'number' sub-command Ruslan Trofymenko
@ 2019-01-15 10:04 ` Igor Opaniuk
0 siblings, 0 replies; 18+ messages in thread
From: Igor Opaniuk @ 2019-01-15 10:04 UTC (permalink / raw)
To: u-boot
Reviewed-by: Igor Opaniuk <igor.opaniuk@linaro.org>
On Wed, 12 Dec 2018 at 19:12, Ruslan Trofymenko
<ruslan.trofymenko@linaro.org> wrote:
>
> This sub-command serves for getting the partition index from
> partition name. Also it can be used to test the existence of specified
> partition.
>
> Signed-off-by: Ruslan Trofymenko <ruslan.trofymenko@linaro.org>
> Reviewed-by: Alistair Strachan <astrachan@google.com>
> Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
> Reviewed-by: Simon Glass <sjg@chromium.org>
> ---
> Changes in v2: None
>
> cmd/part.c | 16 +++++++++++++++-
> 1 file changed, 15 insertions(+), 1 deletion(-)
>
> diff --git a/cmd/part.c b/cmd/part.c
> index bfb6488..653e13c 100644
> --- a/cmd/part.c
> +++ b/cmd/part.c
> @@ -24,6 +24,7 @@
> enum cmd_part_info {
> CMD_PART_INFO_START = 0,
> CMD_PART_INFO_SIZE,
> + CMD_PART_INFO_NUMBER
> };
>
> static int do_part_uuid(int argc, char * const argv[])
> @@ -149,6 +150,9 @@ static int do_part_info(int argc, char * const argv[], enum cmd_part_info param)
> case CMD_PART_INFO_SIZE:
> snprintf(buf, sizeof(buf), LBAF, info.size);
> break;
> + case CMD_PART_INFO_NUMBER:
> + snprintf(buf, sizeof(buf), "%d", part);
> + break;
> default:
> printf("** Unknown cmd_part_info value: %d\n", param);
> return 1;
> @@ -172,6 +176,11 @@ static int do_part_size(int argc, char * const argv[])
> return do_part_info(argc, argv, CMD_PART_INFO_SIZE);
> }
>
> +static int do_part_number(int argc, char * const argv[])
> +{
> + return do_part_info(argc, argv, CMD_PART_INFO_NUMBER);
> +}
> +
> static int do_part(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> {
> if (argc < 2)
> @@ -185,6 +194,8 @@ static int do_part(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> return do_part_start(argc - 2, argv + 2);
> else if (!strcmp(argv[1], "size"))
> return do_part_size(argc - 2, argv + 2);
> + else if (!strcmp(argv[1], "number"))
> + return do_part_number(argc - 2, argv + 2);
>
> return CMD_RET_USAGE;
> }
> @@ -206,5 +217,8 @@ U_BOOT_CMD(
> " part can be either partition number or partition name\n"
> "part size <interface> <dev> <part> <varname>\n"
> " - set environment variable to the size of the partition (in blocks)\n"
> - " part can be either partition number or partition name"
> + " part can be either partition number or partition name\n"
> + "part number <interface> <dev> <part> <varname>\n"
> + " - set environment variable to the partition number using the partition name\n"
> + " part must be specified as partition name"
> );
> --
> 2.7.4
>
--
Regards,
Igor Opaniuk
^ permalink raw reply [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 6/7] doc: android: Add simple guide for A/B updates
2018-12-12 17:12 ` [U-Boot] [PATCH v2 6/7] doc: android: Add simple guide " Ruslan Trofymenko
2019-01-05 1:56 ` Simon Glass
@ 2019-01-15 10:20 ` Igor Opaniuk
1 sibling, 0 replies; 18+ messages in thread
From: Igor Opaniuk @ 2019-01-15 10:20 UTC (permalink / raw)
To: u-boot
Reviewed-by: Igor Opaniuk <igor.opaniuk@linaro.org>
On Wed, 12 Dec 2018 at 19:12, Ruslan Trofymenko
<ruslan.trofymenko@linaro.org> wrote:
>
> Add a short documentation for A/B enablement and 'ab_select' command
> usage.
>
> Signed-off-by: Ruslan Trofymenko <ruslan.trofymenko@linaro.org>
> Reviewed-by: Alistair Strachan <astrachan@google.com>
> Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
> ---
> Changes in v2:
> * Сhanges related to command renaming (android_ab_select -> ab_select)
>
> 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 0000000..9f37ed5
> --- /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.7.4
>
--
Regards,
Igor Opaniuk
^ permalink raw reply [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 4/7] cmd: Add 'ab_select' command
2018-12-12 17:12 ` [U-Boot] [PATCH v2 4/7] cmd: Add 'ab_select' command Ruslan Trofymenko
@ 2019-01-15 10:46 ` Igor Opaniuk
0 siblings, 0 replies; 18+ messages in thread
From: Igor Opaniuk @ 2019-01-15 10:46 UTC (permalink / raw)
To: u-boot
Hello Ruslan,
On Wed, 12 Dec 2018 at 19:12, Ruslan Trofymenko
<ruslan.trofymenko@linaro.org> wrote:
>
> 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.
minor: so why not to add Alistair's Singed-off-by tag also?
>
> [1] https://android-review.googlesource.com/c/platform/external/u-boot/+/729880/2
>
> Signed-off-by: Ruslan Trofymenko <ruslan.trofymenko@linaro.org>
> Reviewed-by: Alistair Strachan <astrachan@google.com>
> Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
> ---
> 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 ea1a325..ed60e1e 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -1123,6 +1123,21 @@ config CMD_SETEXPR
>
> endmenu
>
> +menu "Android support commands"
as a suggestion: use existing menu "Boot 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 4998643..1d345c1 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 0000000..2a9e524
> --- /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;
> + }
minor: probably checkpatch will argue and ask to remove the check for
{} on single line if.
> +
> + 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] = ANDROID_BOOT_SLOT_NAME(ret);
> + slot[1] = '\0';
> + env_set(argv[1], slot);
> + printf("ANDROID: Booting slot: %s\n", slot);
minor: too much stuff in a row, better to split filling of slot
buffer, environment variable setting and return with blank lines.
> + 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"
> +);
> --
> 2.7.4
>
With/without my minor comments addressed:
Reviewed-by: Igor Opaniuk <igor.opaniuk@linaro.org>
--
Regards,
Igor Opaniuk
^ permalink raw reply [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 3/7] common: Implement A/B metadata
2018-12-12 17:12 ` [U-Boot] [PATCH v2 3/7] common: Implement A/B metadata Ruslan Trofymenko
@ 2019-01-15 11:38 ` Igor Opaniuk
0 siblings, 0 replies; 18+ messages in thread
From: Igor Opaniuk @ 2019-01-15 11:38 UTC (permalink / raw)
To: u-boot
Hi,
On Wed, 12 Dec 2018 at 19:12, Ruslan Trofymenko
<ruslan.trofymenko@linaro.org> wrote:
>
> 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
> [2] https://android-review.googlesource.com/c/platform/external/u-boot/+/729880/2
>
> Signed-off-by: Ruslan Trofymenko <ruslan.trofymenko@linaro.org>
> Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
> ---
> 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 | 277 +++++++++++++++++++++++++++++++++++++++++++++++
> include/android_ab.h | 34 ++++++
> include/android_bl_msg.h | 169 +++++++++++++++++++++++++++++
> 5 files changed, 491 insertions(+)
> create mode 100644 common/android_ab.c
> create mode 100644 include/android_ab.h
> create mode 100644 include/android_bl_msg.h
>
> diff --git a/common/Kconfig b/common/Kconfig
> index 57bd16d..0ff4679 100644
> --- a/common/Kconfig
> +++ b/common/Kconfig
> @@ -748,6 +748,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 65d89dc..acc3851 100644
> --- a/common/Makefile
> +++ b/common/Makefile
> @@ -104,6 +104,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 0000000..dda2263
> --- /dev/null
> +++ b/common/android_ab.c
> @@ -0,0 +1,277 @@
> +// SPDX-License-Identifier: BSD-2-Clause
> +/*
> + * Copyright (C) 2017 The Android Open Source Project
> + */
> +
> +#include <common.h>
> +#include <android_ab.h>
> +#include <android_bl_msg.h>
> +#include <memalign.h>
> +#include <u-boot/crc.h>
Please sort include files in the alphabetical order.
> +
> +/**
> + * 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 ab_control_compute_crc(struct andr_bl_control *abc)
> +{
> + return crc32(0, (void *)abc, offsetof(typeof(*abc), crc32_le));
> +}
> +
> +/**
> + * Initialize andr_bl_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.
> + */
> +static void ab_control_default(struct andr_bl_control *abc)
> +{
> + int i;
> + const struct andr_slot_metadata metadata = {
> + .priority = 15,
> + .tries_remaining = 7,
> + .successful_boot = 0,
> + .verity_corrupted = 0,
> + .reserved = 0
> + };
Please check abc for a sane value.
> + memcpy(abc->slot_suffix, "a\0\0\0", 4);
Why not to memset with zeros initially the whole andr_bl_control
struct instead of fancy way of filling it with strings like "a\0\0\0"?
> + 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 = ab_control_compute_crc(abc);
> +}
> +
> +/**
> + * 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 andr_bl_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 andr_bl_control **abc)
> +{
> + ulong abc_offset, abc_blocks;
> +
> + abc_offset = offsetof(struct andr_bl_msg_ab, slot_suffix);
> + if (abc_offset % part_info->blksz) {
please check part_info if it's not null before using it.
> + printf("ANDROID: Boot control block not block aligned.\n");
> + return -EINVAL;
> + }
> + abc_offset /= part_info->blksz;
> +
> + abc_blocks = DIV_ROUND_UP(sizeof(struct andr_bl_control),
> + part_info->blksz);
> + if (abc_offset + abc_blocks > part_info->size) {
> + printf("ANDROID: boot control partition too small. Need at");
> + printf(" 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);
it would be good to check abc for NULL before dereferencing.
> + if (!*abc)
> + return -ENOMEM;
> +
> + if (blk_dread(dev_desc, part_info->start + abc_offset, abc_blocks,
> + *abc) != abc_blocks) {
> + printf("ANDROID: Could not read from boot control partition\n");
> + free(*abc);
> + return -EIO;
> + }
> + log_debug("ANDROID: Loaded ABC, %lu blocks\n", abc_blocks);
blank line
> + 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 andr_bl_control *abc)
> +{
> + ulong abc_offset, abc_blocks;
> +
> + abc_offset = offsetof(struct andr_bl_msg_ab, slot_suffix) /
> + part_info->blksz;
> + abc_blocks = DIV_ROUND_UP(sizeof(struct andr_bl_control),
> + part_info->blksz);
> + if (blk_dwrite(dev_desc, part_info->start + abc_offset, abc_blocks,
> + abc) != abc_blocks) {
> + printf("ANDROID: Could not write back the misc partition\n");
> + return -EIO;
> + }
blank line
> + 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 andr_slot_metadata *a,
> + const struct andr_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 andr_bl_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 (!abc || ret < 0) {
check additionally for abc is redundant, as
ab_control_create_from_disk is doing this implicitly and will just
return code -ENOMEM.
> + /*
> + * 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;
> + }
no need to wrap with {}
> +
> + crc32_le = ab_control_compute_crc(abc);
> + if (abc->crc32_le != crc32_le) {
> + printf("ANDROID: Invalid CRC-32 (expected %.8x, found %.8x), ",
> + crc32_le, abc->crc32_le);
> + printf("re-initializing A/B metadata.\n");
> + ab_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 -ENODATA;
> + }
> +
> + if (abc->version > ANDROID_BOOT_CTRL_VERSION) {
> + printf("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) {
> + 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 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 = 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 0000000..c1b901d
> --- /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 ANDROID_BOOT_SLOT_NAME(slot_num) ('a' + (slot_num))
> +
> +/* Number of slots */
> +#define ANDROID_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 */
> diff --git a/include/android_bl_msg.h b/include/android_bl_msg.h
> new file mode 100644
> index 0000000..f37e01a
> --- /dev/null
> +++ b/include/android_bl_msg.h
> @@ -0,0 +1,169 @@
> +/* SPDX-License-Identifier: BSD-2-Clause */
> +/*
> + * This file was taken from the AOSP Project.
> + * Repository: https://android.googlesource.com/platform/bootable/recovery/
> + * File: bootloader_message/include/bootloader_message/bootloader_message.h
> + * Commit: 8b309f6970ab3b7c53cc529c51a2cb44e1c7a7e1
> + *
> + * Copyright (C) 2008 The Android Open Source Project
> + */
> +
> +#ifndef __ANDROID_BL_MSG_H
> +#define __ANDROID_BL_MSG_H
> +
> +/*
> + * compiler.h defines the types that otherwise are included from stdint.h and
> + * stddef.h
> + */
> +#include <compiler.h>
> +#include <linux/sizes.h>
> +
> +/*
> + * Spaces used by misc partition are as below:
> + * 0 - 2K Bootloader Message
> + * 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally
> + * used as bootloader_message_ab struct)
> + * 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B
> + * devices
> + * Note that these offsets are admitted by bootloader, recovery and uncrypt, so
> + * they are not configurable without changing all of them.
> + */
> +#define ANDROID_MISC_BM_OFFSET 0
> +#define ANDROID_MISC_WIPE_OFFSET SZ_16K
> +
> +/**
> + * Bootloader Message (2-KiB).
> + *
> + * This structure describes the content of a block in flash
> + * that is used for recovery and the bootloader to talk to
> + * each other.
> + *
> + * The command field is updated by linux when it wants to
> + * reboot into recovery or to update radio or bootloader firmware.
> + * It is also updated by the bootloader when firmware update
> + * is complete (to boot into recovery for any final cleanup)
> + *
> + * The status field is written by the bootloader after the
> + * completion of an "update-radio" or "update-hboot" command.
> + *
> + * The recovery field is only written by linux and used
> + * for the system to send a message to recovery or the
> + * other way around.
> + *
> + * The stage field is written by packages which restart themselves
> + * multiple times, so that the UI can reflect which invocation of the
> + * package it is. If the value is of the format "#/#" (eg, "1/3"),
> + * the UI will add a simple indicator of that status.
> + *
> + * We used to have slot_suffix field for A/B boot control metadata in
> + * this struct, which gets unintentionally cleared by recovery or
> + * uncrypt. Move it into struct bootloader_message_ab to avoid the
> + * issue.
> + */
> +struct andr_bl_msg {
> + char command[32];
> + char status[32];
> + char recovery[768];
> +
> + /*
> + * The 'recovery' field used to be 1024 bytes. It has only ever
> + * been used to store the recovery command line, so 768 bytes
> + * should be plenty. We carve off the last 256 bytes to store the
> + * stage string (for multistage packages) and possible future
> + * expansion.
> + */
> + char stage[32];
> +
> + /*
> + * The 'reserved' field used to be 224 bytes when it was initially
> + * carved off from the 1024-byte recovery field. Bump it up to
> + * 1184-byte so that the entire bootloader_message struct rounds up
> + * to 2048-byte.
> + */
> + char reserved[1184];
> +};
> +
> +/**
> + * The A/B-specific bootloader message structure (4-KiB).
> + *
> + * We separate A/B boot control metadata from the regular bootloader
> + * message struct and keep it here. Everything that's A/B-specific
> + * stays after struct bootloader_message, which should be managed by
> + * the A/B-bootloader or boot control HAL.
> + *
> + * The slot_suffix field is used for A/B implementations where the
> + * bootloader does not set the androidboot.ro.boot.slot_suffix kernel
> + * commandline parameter. This is used by fs_mgr to mount /system and
> + * other partitions with the slotselect flag set in fstab. A/B
> + * implementations are free to use all 32 bytes and may store private
> + * data past the first NUL-byte in this field. It is encouraged, but
> + * not mandatory, to use 'struct bootloader_control' described below.
> + *
> + * The update_channel field is used to store the Omaha update channel
> + * if update_engine is compiled with Omaha support.
> + */
> +struct andr_bl_msg_ab {
> + struct andr_bl_msg message;
> + char slot_suffix[32];
> + char update_channel[128];
> +
> + /* Round up the entire struct to 4096-byte */
> + char reserved[1888];
> +};
> +
> +#define ANDROID_BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */
> +#define ANDROID_BOOT_CTRL_VERSION 1
> +
> +struct andr_slot_metadata {
> + /*
> + * Slot priority with 15 meaning highest priority, 1 lowest
> + * priority and 0 the slot is unbootable
> + */
> + u8 priority : 4;
> + /* Number of times left attempting to boot this slot */
> + u8 tries_remaining : 3;
> + /* 1 if this slot has booted successfully, 0 otherwise */
> + u8 successful_boot : 1;
> + /*
> + * 1 if this slot is corrupted from a dm-verity corruption,
> + * 0 otherwise
> + */
> + u8 verity_corrupted : 1;
> + /* Reserved for further use */
> + u8 reserved : 7;
> +} __packed;
> +
> +/**
> + * Bootloader Control AB.
> + *
> + * This struct can be used to manage A/B metadata. It is designed to
> + * be put in the 'slot_suffix' field of the 'bootloader_message'
> + * structure described above. It is encouraged to use the
> + * 'bootloader_control' structure to store the A/B metadata, but not
> + * mandatory.
> + */
> +struct andr_bl_control {
> + /* NULL terminated active slot suffix */
> + char slot_suffix[4];
> + /* Bootloader Control AB magic number (see BOOT_CTRL_MAGIC) */
> + u32 magic;
> + /* Version of struct being used (see BOOT_CTRL_VERSION) */
> + u8 version;
> + /* Number of slots being managed */
> + u8 nb_slot : 3;
> + /* Number of times left attempting to boot recovery */
> + u8 recovery_tries_remaining : 3;
> + /* Ensure 4-bytes alignment for slot_info field */
> + u8 reserved0[2];
> + /* Per-slot information. Up to 4 slots */
> + struct andr_slot_metadata slot_info[4];
> + /* Reserved for further use */
> + u8 reserved1[8];
> + /*
> + * CRC32 of all 28 bytes preceding this field (little endian
> + * format)
> + */
> + u32 crc32_le;
> +} __packed;
> +
> +#endif /* __ANDROID_BL_MSG_H */
> --
> 2.7.4
>
--
Regards,
Igor Opaniuk
^ permalink raw reply [flat|nested] 18+ messages in thread
* [U-Boot] [PATCH v2 7/7] env: am57xx: Implement A/B boot process
2018-12-12 17:12 ` [U-Boot] [PATCH v2 7/7] env: am57xx: Implement A/B boot process Ruslan Trofymenko
2019-01-05 1:56 ` Simon Glass
@ 2019-01-15 11:42 ` Igor Opaniuk
1 sibling, 0 replies; 18+ messages in thread
From: Igor Opaniuk @ 2019-01-15 11:42 UTC (permalink / raw)
To: u-boot
Reviewed-by: Igor Opaniuk <igor.opaniuk@linaro.org>
On Wed, 12 Dec 2018 at 19:12, Ruslan Trofymenko
<ruslan.trofymenko@linaro.org> wrote:
>
> 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>
> Reviewed-by: Alistair Strachan <astrachan@google.com>
> Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
> ---
> Changes in v2:
> * Сhanges 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 5891009..e3ba689 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 \
> @@ -40,8 +52,7 @@
> "name=efs,size=16M,uuid=${uuid_gpt_efs};" \
> "name=crypto,size=16K,uuid=${uuid_gpt_crypto};" \
> "name=recovery,size=40M,uuid=${uuid_gpt_recovery};" \
> - "name=boot,size=10M,uuid=${uuid_gpt_boot};" \
> - "name=system,size=768M,uuid=${uuid_gpt_system};" \
> + COMMON_PARTS \
> "name=vendor,size=256M,uuid=${uuid_gpt_vendor};" \
> "name=cache,size=256M,uuid=${uuid_gpt_cache};" \
> "name=ipu1,size=1M,uuid=${uuid_gpt_ipu1};" \
> @@ -63,6 +74,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" \
> @@ -91,10 +131,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.7.4
>
--
Regards,
Igor Opaniuk
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2019-01-15 11:42 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-12 17:12 [U-Boot] [PATCH v2 0/7] android: Implement A/B boot process Ruslan Trofymenko
2018-12-12 17:12 ` [U-Boot] [PATCH v2 1/7] cmd: part: Add 'number' sub-command Ruslan Trofymenko
2019-01-15 10:04 ` Igor Opaniuk
2018-12-12 17:12 ` [U-Boot] [PATCH v2 2/7] disk: part: Extend API to get partition info Ruslan Trofymenko
2019-01-05 1:56 ` Simon Glass
2018-12-12 17:12 ` [U-Boot] [PATCH v2 3/7] common: Implement A/B metadata Ruslan Trofymenko
2019-01-15 11:38 ` Igor Opaniuk
2018-12-12 17:12 ` [U-Boot] [PATCH v2 4/7] cmd: Add 'ab_select' command Ruslan Trofymenko
2019-01-15 10:46 ` Igor Opaniuk
2018-12-12 17:12 ` [U-Boot] [PATCH v2 5/7] test/py: Add base test case for A/B updates Ruslan Trofymenko
2019-01-05 1:56 ` Simon Glass
2018-12-12 17:12 ` [U-Boot] [PATCH v2 6/7] doc: android: Add simple guide " Ruslan Trofymenko
2019-01-05 1:56 ` Simon Glass
2019-01-15 10:20 ` Igor Opaniuk
2018-12-12 17:12 ` [U-Boot] [PATCH v2 7/7] env: am57xx: Implement A/B boot process Ruslan Trofymenko
2019-01-05 1:56 ` Simon Glass
2019-01-15 11:42 ` Igor Opaniuk
2018-12-17 13:05 ` [U-Boot] [PATCH v2 0/7] android: " Sam Protsenko
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.