summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2019-07-24 21:15:51 +0300
committerTom Rini <trini@konsulko.com>2019-07-24 21:15:51 +0300
commita9aa4c5700c68c070d63a391b51ea8d341b6e8a6 (patch)
tree974a824402b489cfec95f2ca8ec5bd9fef71a8bc
parentef783259d6a558d31385039fa0def211fa7ad032 (diff)
parente21e3ffdd18b88a12d98f6ae985fd5d334b9cec9 (diff)
downloadu-boot-a9aa4c5700c68c070d63a391b51ea8d341b6e8a6.tar.xz
Merge branch '2019-07-24-master-imports'
- Various Android related changes including A/B update and BCB updates - Assorted minor fixes
-rw-r--r--Documentation/.gitignore1
-rw-r--r--arch/arm/cpu/armv7/sunxi/psci.c2
-rw-r--r--arch/arm/include/asm/system.h15
-rw-r--r--arch/arm/mach-imx/mx7/psci-mx7.c2
-rw-r--r--arch/arm/mach-stm32mp/psci.c18
-rw-r--r--arch/arm/mach-uniphier/arm32/psci.c4
-rw-r--r--cmd/Kconfig17
-rw-r--r--cmd/Makefile1
-rw-r--r--cmd/ab_select.c52
-rw-r--r--cmd/bcb.c68
-rw-r--r--cmd/part.c16
-rw-r--r--common/Kconfig10
-rw-r--r--common/Makefile1
-rw-r--r--common/android_ab.c300
-rw-r--r--common/image-android.c22
-rw-r--r--common/menu.c6
-rw-r--r--common/spl/Kconfig14
-rw-r--r--common/spl/spl_sata.c34
-rw-r--r--configs/sandbox_defconfig2
-rw-r--r--disk/part.c68
-rw-r--r--doc/.gitignore1
-rw-r--r--doc/android/ab.txt67
-rw-r--r--doc/conf.py2
-rw-r--r--doc/sphinx/kerneldoc.py12
-rw-r--r--doc/sphinx/kernellog.py28
-rw-r--r--doc/sphinx/kfigure.py40
-rw-r--r--doc/uImage.FIT/signature.txt7
-rw-r--r--include/android_ab.h34
-rw-r--r--include/config_distro_bootcmd.h27
-rw-r--r--include/environment/ti/boot.h58
-rw-r--r--include/part.h21
-rw-r--r--include/remoteproc.h2
-rwxr-xr-xscripts/kernel-doc39
-rw-r--r--test/py/tests/test_android/test_ab.py75
-rw-r--r--test/py/tests/test_avb.py2
35 files changed, 954 insertions, 114 deletions
diff --git a/Documentation/.gitignore b/Documentation/.gitignore
index e74fec8693..0d20b6487c 100644
--- a/Documentation/.gitignore
+++ b/Documentation/.gitignore
@@ -1,2 +1 @@
-output
*.pyc
diff --git a/arch/arm/cpu/armv7/sunxi/psci.c b/arch/arm/cpu/armv7/sunxi/psci.c
index f3e8f99a71..2c5d99e9ac 100644
--- a/arch/arm/cpu/armv7/sunxi/psci.c
+++ b/arch/arm/cpu/armv7/sunxi/psci.c
@@ -276,7 +276,7 @@ int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc,
return ARM_PSCI_RET_SUCCESS;
}
-void __secure psci_cpu_off(void)
+s32 __secure psci_cpu_off(void)
{
psci_cpu_off_common();
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h
index aed2e3c51e..a1a5e35ef6 100644
--- a/arch/arm/include/asm/system.h
+++ b/arch/arm/include/asm/system.h
@@ -516,6 +516,21 @@ enum {
*/
void mmu_page_table_flush(unsigned long start, unsigned long stop);
+#ifdef CONFIG_ARMV7_PSCI
+void psci_arch_cpu_entry(void);
+u32 psci_version(void);
+s32 psci_features(u32 function_id, u32 psci_fid);
+s32 psci_cpu_off(void);
+s32 psci_cpu_on(u32 function_id, u32 target_cpu, u32 pc,
+ u32 context_id);
+s32 psci_affinity_info(u32 function_id, u32 target_affinity,
+ u32 lowest_affinity_level);
+u32 psci_migrate_info_type(void);
+void psci_system_off(void);
+void psci_system_reset(void);
+s32 psci_features(u32 function_id, u32 psci_fid);
+#endif
+
#endif /* __ASSEMBLY__ */
#define arch_align_stack(x) (x)
diff --git a/arch/arm/mach-imx/mx7/psci-mx7.c b/arch/arm/mach-imx/mx7/psci-mx7.c
index 34ba0a9307..c98d2e96af 100644
--- a/arch/arm/mach-imx/mx7/psci-mx7.c
+++ b/arch/arm/mach-imx/mx7/psci-mx7.c
@@ -298,7 +298,7 @@ __secure s32 psci_affinity_info(u32 __always_unused function_id,
return psci_state[cpu];
}
-__secure s32 psci_migrate_info_type(u32 function_id)
+__secure u32 psci_migrate_info_type(void)
{
/* Trusted OS is either not present or does not require migration */
return 2;
diff --git a/arch/arm/mach-stm32mp/psci.c b/arch/arm/mach-stm32mp/psci.c
index 139bb09292..1d91b2d324 100644
--- a/arch/arm/mach-stm32mp/psci.c
+++ b/arch/arm/mach-stm32mp/psci.c
@@ -30,7 +30,7 @@ u8 psci_state[STM32MP1_PSCI_NR_CPUS] __secure_data = {
PSCI_AFFINITY_LEVEL_ON,
PSCI_AFFINITY_LEVEL_OFF};
-void __secure psci_set_state(int cpu, u8 state)
+static inline void psci_set_state(int cpu, u8 state)
{
psci_state[cpu] = state;
dsb();
@@ -67,7 +67,7 @@ void __secure psci_arch_cpu_entry(void)
writel(0xFFFFFFFF, TAMP_BACKUP_MAGIC_NUMBER);
}
-int __secure psci_features(u32 function_id, u32 psci_fid)
+s32 __secure psci_features(u32 function_id, u32 psci_fid)
{
switch (psci_fid) {
case ARM_PSCI_0_2_FN_PSCI_VERSION:
@@ -82,12 +82,12 @@ int __secure psci_features(u32 function_id, u32 psci_fid)
return ARM_PSCI_RET_NI;
}
-unsigned int __secure psci_version(u32 function_id)
+u32 __secure psci_version(void)
{
return ARM_PSCI_VER_1_0;
}
-int __secure psci_affinity_info(u32 function_id, u32 target_affinity,
+s32 __secure psci_affinity_info(u32 function_id, u32 target_affinity,
u32 lowest_affinity_level)
{
u32 cpu = target_affinity & MPIDR_AFF0;
@@ -104,7 +104,7 @@ int __secure psci_affinity_info(u32 function_id, u32 target_affinity,
return psci_state[cpu];
}
-int __secure psci_migrate_info_type(u32 function_id)
+u32 __secure psci_migrate_info_type(void)
{
/*
* in Power_State_Coordination_Interface_PDD_v1_1_DEN0022D.pdf
@@ -116,7 +116,7 @@ int __secure psci_migrate_info_type(u32 function_id)
return 2;
}
-int __secure psci_cpu_on(u32 function_id, u32 target_cpu, u32 pc,
+s32 __secure psci_cpu_on(u32 function_id, u32 target_cpu, u32 pc,
u32 context_id)
{
u32 cpu = target_cpu & MPIDR_AFF0;
@@ -161,7 +161,7 @@ int __secure psci_cpu_on(u32 function_id, u32 target_cpu, u32 pc,
return ARM_PSCI_RET_SUCCESS;
}
-int __secure psci_cpu_off(u32 function_id)
+s32 __secure psci_cpu_off(void)
{
u32 cpu;
@@ -181,7 +181,7 @@ int __secure psci_cpu_off(u32 function_id)
wfi();
}
-void __secure psci_system_reset(u32 function_id)
+void __secure psci_system_reset(void)
{
/* System reset */
writel(RCC_MP_GRSTCSETR_MPSYSRST, RCC_MP_GRSTCSETR);
@@ -190,7 +190,7 @@ void __secure psci_system_reset(u32 function_id)
wfi();
}
-void __secure psci_system_off(u32 function_id)
+void __secure psci_system_off(void)
{
/* System Off is not managed, waiting user power off
* TODO: handle I2C write in PMIC Main Control register bit 0 = SWOFF
diff --git a/arch/arm/mach-uniphier/arm32/psci.c b/arch/arm/mach-uniphier/arm32/psci.c
index 3f67edf26e..ef35923f6a 100644
--- a/arch/arm/mach-uniphier/arm32/psci.c
+++ b/arch/arm/mach-uniphier/arm32/psci.c
@@ -130,7 +130,7 @@ void psci_arch_init(void)
u32 uniphier_psci_holding_pen_release __secure_data = 0xffffffff;
-int __secure psci_cpu_on(u32 function_id, u32 cpuid, u32 entry_point,
+s32 __secure psci_cpu_on(u32 function_id, u32 cpuid, u32 entry_point,
u32 context_id)
{
u32 cpu = cpuid & 0xff;
@@ -155,7 +155,7 @@ int __secure psci_cpu_on(u32 function_id, u32 cpuid, u32 entry_point,
return PSCI_RET_SUCCESS;
}
-void __secure psci_system_reset(u32 function_id)
+void __secure psci_system_reset(void)
{
reset_cpu(0);
}
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 175c6ad9e3..9e66cc110d 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -735,7 +735,7 @@ config CMD_FASTBOOT
Android devices. Fastboot requires either the network stack
enabled or support for acting as a USB device.
- See doc/README.android-fastboot for more information.
+ See doc/android/fastboot.txt for more information.
config CMD_FDC
bool "fdcboot - Boot from floppy device"
@@ -1198,6 +1198,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 0aa3741453..43a6b0ee21 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -12,6 +12,7 @@ obj-y += version.o
# command
obj-$(CONFIG_CMD_AES) += aes.o
+obj-$(CONFIG_CMD_AB_SELECT) += ab_select.o
obj-$(CONFIG_CMD_ADC) += adc.o
obj-$(CONFIG_CMD_ARMFLASH) += armflash.o
obj-y += blk_common.o
diff --git a/cmd/ab_select.c b/cmd/ab_select.c
new file mode 100644
index 0000000000..7c8f2ee8eb
--- /dev/null
+++ b/cmd/ab_select.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ */
+
+#include <android_ab.h>
+#include <command.h>
+
+static int do_ab_select(cmd_tbl_t *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ int ret;
+ struct blk_desc *dev_desc;
+ disk_partition_t part_info;
+ char slot[2];
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ /* Lookup the "misc" partition from argv[2] and argv[3] */
+ if (part_get_info_by_dev_and_name_or_num(argv[2], argv[3],
+ &dev_desc, &part_info) < 0) {
+ return CMD_RET_FAILURE;
+ }
+
+ ret = ab_select_slot(dev_desc, &part_info);
+ if (ret < 0) {
+ printf("Android boot failed, error %d.\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ /* Android standard slot names are 'a', 'b', ... */
+ slot[0] = BOOT_SLOT_NAME(ret);
+ slot[1] = '\0';
+ env_set(argv[1], slot);
+ printf("ANDROID: Booting slot: %s\n", slot);
+ return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(ab_select, 4, 0, do_ab_select,
+ "Select the slot used to boot from and register the boot attempt.",
+ "<slot_var_name> <interface> <dev[:part|#part_name]>\n"
+ " - Load the slot metadata from the partition 'part' on\n"
+ " device type 'interface' instance 'dev' and store the active\n"
+ " slot in the 'slot_var_name' variable. This also updates the\n"
+ " Android slot metadata with a boot attempt, which can cause\n"
+ " successive calls to this function to return a different result\n"
+ " if the returned slot runs out of boot attempts.\n"
+ " - If 'part_name' is passed, preceded with a # instead of :, the\n"
+ " partition name whose label is 'part_name' will be looked up in\n"
+ " the partition table. This is commonly the \"misc\" partition.\n"
+);
diff --git a/cmd/bcb.c b/cmd/bcb.c
index 2bd5a744de..9626f2c69e 100644
--- a/cmd/bcb.c
+++ b/cmd/bcb.c
@@ -24,17 +24,17 @@ static struct bootloader_message bcb = { { 0 } };
static int bcb_cmd_get(char *cmd)
{
- if (!strncmp(cmd, "load", sizeof("load")))
+ if (!strcmp(cmd, "load"))
return BCB_CMD_LOAD;
- if (!strncmp(cmd, "set", sizeof("set")))
+ if (!strcmp(cmd, "set"))
return BCB_CMD_FIELD_SET;
- if (!strncmp(cmd, "clear", sizeof("clear")))
+ if (!strcmp(cmd, "clear"))
return BCB_CMD_FIELD_CLEAR;
- if (!strncmp(cmd, "test", sizeof("test")))
+ if (!strcmp(cmd, "test"))
return BCB_CMD_FIELD_TEST;
- if (!strncmp(cmd, "store", sizeof("store")))
+ if (!strcmp(cmd, "store"))
return BCB_CMD_STORE;
- if (!strncmp(cmd, "dump", sizeof("dump")))
+ if (!strcmp(cmd, "dump"))
return BCB_CMD_FIELD_DUMP;
else
return -1;
@@ -46,9 +46,6 @@ static int bcb_is_misused(int argc, char *const argv[])
switch (cmd) {
case BCB_CMD_LOAD:
- if (argc != 3)
- goto err;
- break;
case BCB_CMD_FIELD_SET:
if (argc != 3)
goto err;
@@ -86,23 +83,23 @@ err:
return -1;
}
-static int bcb_field_get(char *name, char **field, int *size)
+static int bcb_field_get(char *name, char **fieldp, int *sizep)
{
- if (!strncmp(name, "command", sizeof("command"))) {
- *field = bcb.command;
- *size = sizeof(bcb.command);
- } else if (!strncmp(name, "status", sizeof("status"))) {
- *field = bcb.status;
- *size = sizeof(bcb.status);
- } else if (!strncmp(name, "recovery", sizeof("recovery"))) {
- *field = bcb.recovery;
- *size = sizeof(bcb.recovery);
- } else if (!strncmp(name, "stage", sizeof("stage"))) {
- *field = bcb.stage;
- *size = sizeof(bcb.stage);
- } else if (!strncmp(name, "reserved", sizeof("reserved"))) {
- *field = bcb.reserved;
- *size = sizeof(bcb.reserved);
+ if (!strcmp(name, "command")) {
+ *fieldp = bcb.command;
+ *sizep = sizeof(bcb.command);
+ } else if (!strcmp(name, "status")) {
+ *fieldp = bcb.status;
+ *sizep = sizeof(bcb.status);
+ } else if (!strcmp(name, "recovery")) {
+ *fieldp = bcb.recovery;
+ *sizep = sizeof(bcb.recovery);
+ } else if (!strcmp(name, "stage")) {
+ *fieldp = bcb.stage;
+ *sizep = sizeof(bcb.stage);
+ } else if (!strcmp(name, "reserved")) {
+ *fieldp = bcb.reserved;
+ *sizep = sizeof(bcb.reserved);
} else {
printf("Error: Unknown bcb field '%s'\n", name);
return -1;
@@ -111,8 +108,8 @@ static int bcb_field_get(char *name, char **field, int *size)
return 0;
}
-static int
-do_bcb_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+static int do_bcb_load(cmd_tbl_t *cmdtp, int flag, int argc,
+ char * const argv[])
{
struct blk_desc *desc;
disk_partition_t info;
@@ -122,28 +119,28 @@ do_bcb_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
ret = blk_get_device_by_str("mmc", argv[1], &desc);
if (ret < 0)
- goto err_1;
+ goto err_read_fail;
part = simple_strtoul(argv[2], &endp, 0);
if (*endp == '\0') {
ret = part_get_info(desc, part, &info);
if (ret)
- goto err_1;
+ goto err_read_fail;
} else {
part = part_get_info_by_name(desc, argv[2], &info);
if (part < 0) {
ret = part;
- goto err_1;
+ goto err_read_fail;
}
}
cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), info.blksz);
if (cnt > info.size)
- goto err_2;
+ goto err_too_small;
if (blk_dread(desc, info.start, cnt, &bcb) != cnt) {
ret = -EIO;
- goto err_1;
+ goto err_read_fail;
}
bcb_dev = desc->devnum;
@@ -151,10 +148,10 @@ do_bcb_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
debug("%s: Loaded from mmc %d:%d\n", __func__, bcb_dev, bcb_part);
return CMD_RET_SUCCESS;
-err_1:
+err_read_fail:
printf("Error: mmc %s:%s read failed (%d)\n", argv[1], argv[2], ret);
goto err;
-err_2:
+err_too_small:
printf("Error: mmc %s:%s too small!", argv[1], argv[2]);
goto err;
err:
@@ -307,7 +304,8 @@ static int do_bcb(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
return CMD_RET_USAGE;
if (bcb_is_misused(argc, argv)) {
- /* We try to improve the user experience by reporting the
+ /*
+ * We try to improve the user experience by reporting the
* root-cause of misusage, so don't return CMD_RET_USAGE,
* since the latter prints out the full-blown help text
*/
diff --git a/cmd/part.c b/cmd/part.c
index bfb6488b0f..653e13ced1 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"
);
diff --git a/common/Kconfig b/common/Kconfig
index 4865a4dfc8..b556b59e9f 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -821,6 +821,16 @@ config UPDATE_TFTP_MSEC_MAX
default 100
depends on UPDATE_TFTP
+config ANDROID_AB
+ bool "Android A/B updates"
+ default n
+ help
+ If enabled, adds support for the new Android A/B update model. This
+ allows the bootloader to select which slot to boot from based on the
+ information provided by userspace via the Android boot_ctrl HAL. This
+ allows a bootloader to try a new version of the system but roll back
+ to previous version if the new one didn't boot all the way.
+
endmenu
menu "Blob list"
diff --git a/common/Makefile b/common/Makefile
index c7e41ef307..302d8beaf3 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -107,6 +107,7 @@ endif
endif
obj-y += image.o
+obj-$(CONFIG_ANDROID_AB) += android_ab.o
obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o
obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o
obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o
diff --git a/common/android_ab.c b/common/android_ab.c
new file mode 100644
index 0000000000..05ffc6f4e5
--- /dev/null
+++ b/common/android_ab.c
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ */
+#include <common.h>
+#include <android_ab.h>
+#include <android_bootloader_message.h>
+#include <linux/err.h>
+#include <memalign.h>
+#include <u-boot/crc.h>
+
+/**
+ * Compute the CRC-32 of the bootloader control struct.
+ *
+ * Only the bytes up to the crc32_le field are considered for the CRC-32
+ * calculation.
+ *
+ * @param[in] abc bootloader control block
+ *
+ * @return crc32 sum
+ */
+static uint32_t ab_control_compute_crc(struct bootloader_control *abc)
+{
+ return crc32(0, (void *)abc, offsetof(typeof(*abc), crc32_le));
+}
+
+/**
+ * Initialize bootloader_control to the default value.
+ *
+ * It allows us to boot all slots in order from the first one. This value
+ * should be used when the bootloader message is corrupted, but not when
+ * a valid message indicates that all slots are unbootable.
+ *
+ * @param[in] abc bootloader control block
+ *
+ * @return 0 on success and a negative on error
+ */
+static int ab_control_default(struct bootloader_control *abc)
+{
+ int i;
+ const struct slot_metadata metadata = {
+ .priority = 15,
+ .tries_remaining = 7,
+ .successful_boot = 0,
+ .verity_corrupted = 0,
+ .reserved = 0
+ };
+
+ if (!abc)
+ return -EFAULT;
+
+ memcpy(abc->slot_suffix, "a\0\0\0", 4);
+ abc->magic = BOOT_CTRL_MAGIC;
+ abc->version = BOOT_CTRL_VERSION;
+ abc->nb_slot = NUM_SLOTS;
+ memset(abc->reserved0, 0, sizeof(abc->reserved0));
+ for (i = 0; i < abc->nb_slot; ++i)
+ abc->slot_info[i] = metadata;
+
+ memset(abc->reserved1, 0, sizeof(abc->reserved1));
+ abc->crc32_le = ab_control_compute_crc(abc);
+
+ return 0;
+}
+
+/**
+ * Load the boot_control struct from disk into newly allocated memory.
+ *
+ * This function allocates and returns an integer number of disk blocks,
+ * based on the block size of the passed device to help performing a
+ * read-modify-write operation on the boot_control struct.
+ * The boot_control struct offset (2 KiB) must be a multiple of the device
+ * block size, for simplicity.
+ *
+ * @param[in] dev_desc Device where to read the boot_control struct from
+ * @param[in] part_info Partition in 'dev_desc' where to read from, normally
+ * the "misc" partition should be used
+ * @param[out] pointer to pointer to bootloader_control data
+ * @return 0 on success and a negative on error
+ */
+static int ab_control_create_from_disk(struct blk_desc *dev_desc,
+ const disk_partition_t *part_info,
+ struct bootloader_control **abc)
+{
+ ulong abc_offset, abc_blocks, ret;
+
+ abc_offset = offsetof(struct bootloader_message_ab, slot_suffix);
+ if (abc_offset % part_info->blksz) {
+ log_err("ANDROID: Boot control block not block aligned.\n");
+ return -EINVAL;
+ }
+ abc_offset /= part_info->blksz;
+
+ abc_blocks = DIV_ROUND_UP(sizeof(struct bootloader_control),
+ part_info->blksz);
+ if (abc_offset + abc_blocks > part_info->size) {
+ log_err("ANDROID: boot control partition too small. Need at");
+ log_err(" least %lu blocks but have %lu blocks.\n",
+ abc_offset + abc_blocks, part_info->size);
+ return -EINVAL;
+ }
+ *abc = malloc_cache_aligned(abc_blocks * part_info->blksz);
+ if (!*abc)
+ return -ENOMEM;
+
+ ret = blk_dread(dev_desc, part_info->start + abc_offset, abc_blocks,
+ *abc);
+ if (IS_ERR_VALUE(ret)) {
+ log_err("ANDROID: Could not read from boot ctrl partition\n");
+ free(*abc);
+ return -EIO;
+ }
+
+ log_debug("ANDROID: Loaded ABC, %lu blocks\n", abc_blocks);
+
+ return 0;
+}
+
+/**
+ * Store the loaded boot_control block.
+ *
+ * Store back to the same location it was read from with
+ * ab_control_create_from_misc().
+ *
+ * @param[in] dev_desc Device where we should write the boot_control struct
+ * @param[in] part_info Partition on the 'dev_desc' where to write
+ * @param[in] abc Pointer to the boot control struct and the extra bytes after
+ * it up to the nearest block boundary
+ * @return 0 on success and a negative on error
+ */
+static int ab_control_store(struct blk_desc *dev_desc,
+ const disk_partition_t *part_info,
+ struct bootloader_control *abc)
+{
+ ulong abc_offset, abc_blocks, ret;
+
+ abc_offset = offsetof(struct bootloader_message_ab, slot_suffix) /
+ part_info->blksz;
+ abc_blocks = DIV_ROUND_UP(sizeof(struct bootloader_control),
+ part_info->blksz);
+ ret = blk_dwrite(dev_desc, part_info->start + abc_offset, abc_blocks,
+ abc);
+ if (IS_ERR_VALUE(ret)) {
+ log_err("ANDROID: Could not write back the misc partition\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * Compare two slots.
+ *
+ * The function determines slot which is should we boot from among the two.
+ *
+ * @param[in] a The first bootable slot metadata
+ * @param[in] b The second bootable slot metadata
+ * @return Negative if the slot "a" is better, positive of the slot "b" is
+ * better or 0 if they are equally good.
+ */
+static int ab_compare_slots(const struct slot_metadata *a,
+ const struct slot_metadata *b)
+{
+ /* Higher priority is better */
+ if (a->priority != b->priority)
+ return b->priority - a->priority;
+
+ /* Higher successful_boot value is better, in case of same priority */
+ if (a->successful_boot != b->successful_boot)
+ return b->successful_boot - a->successful_boot;
+
+ /* Higher tries_remaining is better to ensure round-robin */
+ if (a->tries_remaining != b->tries_remaining)
+ return b->tries_remaining - a->tries_remaining;
+
+ return 0;
+}
+
+int ab_select_slot(struct blk_desc *dev_desc, disk_partition_t *part_info)
+{
+ struct bootloader_control *abc = NULL;
+ u32 crc32_le;
+ int slot, i, ret;
+ bool store_needed = false;
+ char slot_suffix[4];
+
+ ret = ab_control_create_from_disk(dev_desc, part_info, &abc);
+ if (ret < 0) {
+ /*
+ * This condition represents an actual problem with the code or
+ * the board setup, like an invalid partition information.
+ * Signal a repair mode and do not try to boot from either slot.
+ */
+ return ret;
+ }
+
+ crc32_le = ab_control_compute_crc(abc);
+ if (abc->crc32_le != crc32_le) {
+ log_err("ANDROID: Invalid CRC-32 (expected %.8x, found %.8x),",
+ crc32_le, abc->crc32_le);
+ log_err("re-initializing A/B metadata.\n");
+
+ ret = ab_control_default(abc);
+ if (ret < 0) {
+ free(abc);
+ return -ENODATA;
+ }
+ store_needed = true;
+ }
+
+ if (abc->magic != BOOT_CTRL_MAGIC) {
+ log_err("ANDROID: Unknown A/B metadata: %.8x\n", abc->magic);
+ free(abc);
+ return -ENODATA;
+ }
+
+ if (abc->version > BOOT_CTRL_VERSION) {
+ log_err("ANDROID: Unsupported A/B metadata version: %.8x\n",
+ abc->version);
+ free(abc);
+ return -ENODATA;
+ }
+
+ /*
+ * At this point a valid boot control metadata is stored in abc,
+ * followed by other reserved data in the same block. We select a with
+ * the higher priority slot that
+ * - is not marked as corrupted and
+ * - either has tries_remaining > 0 or successful_boot is true.
+ * If the selected slot has a false successful_boot, we also decrement
+ * the tries_remaining until it eventually becomes unbootable because
+ * tries_remaining reaches 0. This mechanism produces a bootloader
+ * induced rollback, typically right after a failed update.
+ */
+
+ /* Safety check: limit the number of slots. */
+ if (abc->nb_slot > ARRAY_SIZE(abc->slot_info)) {
+ abc->nb_slot = ARRAY_SIZE(abc->slot_info);
+ store_needed = true;
+ }
+
+ slot = -1;
+ for (i = 0; i < abc->nb_slot; ++i) {
+ if (abc->slot_info[i].verity_corrupted ||
+ !abc->slot_info[i].tries_remaining) {
+ log_debug("ANDROID: unbootable slot %d tries: %d, ",
+ i, abc->slot_info[i].tries_remaining);
+ log_debug("corrupt: %d\n",
+ abc->slot_info[i].verity_corrupted);
+ continue;
+ }
+ log_debug("ANDROID: bootable slot %d pri: %d, tries: %d, ",
+ i, abc->slot_info[i].priority,
+ abc->slot_info[i].tries_remaining);
+ log_debug("corrupt: %d, successful: %d\n",
+ abc->slot_info[i].verity_corrupted,
+ abc->slot_info[i].successful_boot);
+
+ if (slot < 0 ||
+ ab_compare_slots(&abc->slot_info[i],
+ &abc->slot_info[slot]) < 0) {
+ slot = i;
+ }
+ }
+
+ if (slot >= 0 && !abc->slot_info[slot].successful_boot) {
+ log_err("ANDROID: Attempting slot %c, tries remaining %d\n",
+ BOOT_SLOT_NAME(slot),
+ abc->slot_info[slot].tries_remaining);
+ abc->slot_info[slot].tries_remaining--;
+ store_needed = true;
+ }
+
+ if (slot >= 0) {
+ /*
+ * Legacy user-space requires this field to be set in the BCB.
+ * Newer releases load this slot suffix from the command line
+ * or the device tree.
+ */
+ memset(slot_suffix, 0, sizeof(slot_suffix));
+ slot_suffix[0] = BOOT_SLOT_NAME(slot);
+ if (memcmp(abc->slot_suffix, slot_suffix,
+ sizeof(slot_suffix))) {
+ memcpy(abc->slot_suffix, slot_suffix,
+ sizeof(slot_suffix));
+ store_needed = true;
+ }
+ }
+
+ if (store_needed) {
+ abc->crc32_le = ab_control_compute_crc(abc);
+ ab_control_store(dev_desc, part_info, abc);
+ }
+ free(abc);
+
+ if (slot < 0)
+ return -EINVAL;
+
+ return slot;
+}
diff --git a/common/image-android.c b/common/image-android.c
index 8b0f6b3b8b..6c9568a655 100644
--- a/common/image-android.c
+++ b/common/image-android.c
@@ -52,6 +52,8 @@ int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify,
ulong *os_data, ulong *os_len)
{
u32 kernel_addr = android_image_get_kernel_addr(hdr);
+ const struct image_header *ihdr = (const struct image_header *)
+ ((uintptr_t)hdr + hdr->page_size);
/*
* Not all Android tools use the id field for signing the image with
@@ -93,11 +95,19 @@ int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify,
env_set("bootargs", newbootargs);
if (os_data) {
- *os_data = (ulong)hdr;
- *os_data += hdr->page_size;
+ if (image_get_magic(ihdr) == IH_MAGIC) {
+ *os_data = image_get_data(ihdr);
+ } else {
+ *os_data = (ulong)hdr;
+ *os_data += hdr->page_size;
+ }
+ }
+ if (os_len) {
+ if (image_get_magic(ihdr) == IH_MAGIC)
+ *os_len = image_get_data_size(ihdr);
+ else
+ *os_len = hdr->kernel_size;
}
- if (os_len)
- *os_len = hdr->kernel_size;
return 0;
}
@@ -131,7 +141,9 @@ ulong android_image_get_kcomp(const struct andr_img_hdr *hdr)
{
const void *p = (void *)((uintptr_t)hdr + hdr->page_size);
- if (get_unaligned_le32(p) == LZ4F_MAGIC)
+ if (image_get_magic((image_header_t *)p) == IH_MAGIC)
+ return image_get_comp((image_header_t *)p);
+ else if (get_unaligned_le32(p) == LZ4F_MAGIC)
return IH_COMP_LZ4;
else
return IH_COMP_NONE;
diff --git a/common/menu.c b/common/menu.c
index 0f0a29ac2e..7b66d199a9 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2010-2011 Calxeda, Inc.
+ * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
*/
#include <common.h>
@@ -39,6 +40,7 @@ struct menu {
char *(*item_choice)(void *);
void *item_choice_data;
struct list_head items;
+ int item_cnt;
};
/*
@@ -271,7 +273,7 @@ int menu_get_choice(struct menu *m, void **choice)
if (!m || !choice)
return -EINVAL;
- if (!m->prompt)
+ if (!m->prompt || m->item_cnt == 1)
return menu_default_choice(m, choice);
return menu_interactive_choice(m, choice);
@@ -323,6 +325,7 @@ int menu_item_add(struct menu *m, char *item_key, void *item_data)
item->data = item_data;
list_add_tail(&item->list, &m->items);
+ m->item_cnt++;
return 1;
}
@@ -374,6 +377,7 @@ struct menu *menu_create(char *title, int timeout, int prompt,
m->item_data_print = item_data_print;
m->item_choice = item_choice;
m->item_choice_data = item_choice_data;
+ m->item_cnt = 0;
if (title) {
m->title = strdup(title);
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index 5978fb2934..5d6da5db89 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -918,6 +918,20 @@ config SPL_SATA_SUPPORT
expense and power consumption. This enables loading from SATA
using a configured device.
+config SPL_SATA_RAW_U_BOOT_USE_SECTOR
+ bool "SATA raw mode: by sector"
+ depends on SPL_SATA_SUPPORT
+ help
+ Use sector number for specifying U-Boot location on SATA disk in
+ raw mode.
+
+config SPL_SATA_RAW_U_BOOT_SECTOR
+ hex "Sector on the SATA disk to load U-Boot from"
+ depends on SPL_SATA_RAW_U_BOOT_USE_SECTOR
+ help
+ Sector on the SATA disk to load U-Boot from, when the SATA disk is being
+ used in raw mode. Units: SATA disk sectors (1 sector = 512 bytes).
+
config SPL_SERIAL_SUPPORT
bool "Support serial"
select SPL_PRINTF
diff --git a/common/spl/spl_sata.c b/common/spl/spl_sata.c
index f0af9f38d1..e108af0576 100644
--- a/common/spl/spl_sata.c
+++ b/common/spl/spl_sata.c
@@ -25,6 +25,37 @@
#define CONFIG_SPL_FS_LOAD_PAYLOAD_NAME "u-boot.img"
#endif
+#ifndef CONFIG_SPL_SATA_RAW_U_BOOT_SECTOR
+/* Dummy value to make the compiler happy */
+#define CONFIG_SPL_SATA_RAW_U_BOOT_SECTOR 0x100
+#endif
+
+static int spl_sata_load_image_raw(struct spl_image_info *spl_image,
+ struct blk_desc *stor_dev, unsigned long sector)
+{
+ struct image_header *header;
+ unsigned long count;
+ u32 image_size_sectors;
+ int ret;
+
+ header = spl_get_load_buffer(-sizeof(*header), stor_dev->blksz);
+ count = blk_dread(stor_dev, sector, 1, header);
+ if (count == 0)
+ return -EIO;
+
+ ret = spl_parse_image_header(spl_image, header);
+ if (ret)
+ return ret;
+
+ image_size_sectors = DIV_ROUND_UP(spl_image->size, stor_dev->blksz);
+ count = blk_dread(stor_dev, sector, image_size_sectors,
+ (void *)spl_image->load_addr);
+ if (count != image_size_sectors)
+ return -EIO;
+
+ return 0;
+}
+
static int spl_sata_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev)
{
@@ -59,6 +90,9 @@ static int spl_sata_load_image(struct spl_image_info *spl_image,
err = spl_load_image_fat(spl_image, stor_dev,
CONFIG_SYS_SATA_FAT_BOOT_PARTITION,
CONFIG_SPL_FS_LOAD_PAYLOAD_NAME);
+ } else if (IS_ENABLED(CONFIG_SPL_SATA_RAW_U_BOOT_USE_SECTOR)) {
+ err = spl_sata_load_image_raw(spl_image, stor_dev,
+ CONFIG_SPL_SATA_RAW_U_BOOT_SECTOR);
}
}
if (err) {
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 61391a7acd..11cc097cd5 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -20,6 +20,7 @@ CONFIG_PRE_CON_BUF_ADDR=0xf0000
CONFIG_LOG_MAX_LEVEL=6
CONFIG_LOG_ERROR_RETURN=y
CONFIG_DISPLAY_BOARDINFO_LATE=y
+CONFIG_ANDROID_AB=y
CONFIG_CMD_CPU=y
CONFIG_CMD_LICENSE=y
CONFIG_CMD_BOOTZ=y
@@ -47,6 +48,7 @@ CONFIG_CMD_REMOTEPROC=y
CONFIG_CMD_SPI=y
CONFIG_CMD_USB=y
CONFIG_CMD_AXI=y
+CONFIG_CMD_AB_SELECT=y
CONFIG_CMD_TFTPPUT=y
CONFIG_CMD_TFTPSRV=y
CONFIG_CMD_RARP=y
diff --git a/disk/part.c b/disk/part.c
index f14bc22b6d..7e84214731 100644
--- a/disk/part.c
+++ b/disk/part.c
@@ -674,6 +674,74 @@ int part_get_info_by_name(struct blk_desc *dev_desc, const char *name,
return part_get_info_by_name_type(dev_desc, name, info, PART_TYPE_ALL);
}
+/**
+ * Get partition info from device number and partition name.
+ *
+ * Parse a device number and partition name string in the form of
+ * "device_num#partition_name", for example "0#misc". If the partition
+ * is found, sets dev_desc and part_info accordingly with the information
+ * of the partition with the given partition_name.
+ *
+ * @param[in] dev_iface Device interface
+ * @param[in] dev_part_str Input string argument, like "0#misc"
+ * @param[out] dev_desc Place to store the device description pointer
+ * @param[out] part_info Place to store the partition information
+ * @return 0 on success, or a negative on error
+ */
+static int part_get_info_by_dev_and_name(const char *dev_iface,
+ const char *dev_part_str,
+ struct blk_desc **dev_desc,
+ disk_partition_t *part_info)
+{
+ char *ep;
+ const char *part_str;
+ int dev_num;
+
+ part_str = strchr(dev_part_str, '#');
+ if (!part_str || part_str == dev_part_str)
+ return -EINVAL;
+
+ dev_num = simple_strtoul(dev_part_str, &ep, 16);
+ if (ep != part_str) {
+ /* Not all the first part before the # was parsed. */
+ return -EINVAL;
+ }
+ part_str++;
+
+ *dev_desc = blk_get_dev(dev_iface, dev_num);
+ if (!*dev_desc) {
+ printf("Could not find %s %d\n", dev_iface, dev_num);
+ return -EINVAL;
+ }
+ if (part_get_info_by_name(*dev_desc, part_str, part_info) < 0) {
+ printf("Could not find \"%s\" partition\n", part_str);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int part_get_info_by_dev_and_name_or_num(const char *dev_iface,
+ const char *dev_part_str,
+ struct blk_desc **dev_desc,
+ disk_partition_t *part_info)
+{
+ /* Split the part_name if passed as "$dev_num#part_name". */
+ if (!part_get_info_by_dev_and_name(dev_iface, dev_part_str,
+ dev_desc, part_info))
+ return 0;
+ /*
+ * Couldn't lookup by name, try looking up the partition description
+ * directly.
+ */
+ if (blk_get_device_part_str(dev_iface, dev_part_str,
+ dev_desc, part_info, 1) < 0) {
+ printf("Couldn't find partition %s %s\n",
+ dev_iface, dev_part_str);
+ return -EINVAL;
+ }
+ return 0;
+}
+
void part_set_generic_name(const struct blk_desc *dev_desc,
int part_num, char *name)
{
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 0000000000..53752db253
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1 @@
+output
diff --git a/doc/android/ab.txt b/doc/android/ab.txt
new file mode 100644
index 0000000000..9f37ed5c58
--- /dev/null
+++ b/doc/android/ab.txt
@@ -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
diff --git a/doc/conf.py b/doc/conf.py
index 168c31346b..0772fb6f0c 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -170,7 +170,7 @@ except ImportError:
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
-#html_logo = None
+html_logo = '../tools/logos/u-boot_logo.svg'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
diff --git a/doc/sphinx/kerneldoc.py b/doc/sphinx/kerneldoc.py
index fbedcc3946..e536360de1 100644
--- a/doc/sphinx/kerneldoc.py
+++ b/doc/sphinx/kerneldoc.py
@@ -39,6 +39,8 @@ from docutils.statemachine import ViewList
from docutils.parsers.rst import directives, Directive
from sphinx.ext.autodoc import AutodocReporter
+import kernellog
+
__version__ = '1.0'
class KernelDocDirective(Directive):
@@ -86,7 +88,8 @@ class KernelDocDirective(Directive):
cmd += [filename]
try:
- env.app.verbose('calling kernel-doc \'%s\'' % (" ".join(cmd)))
+ kernellog.verbose(env.app,
+ 'calling kernel-doc \'%s\'' % (" ".join(cmd)))
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
@@ -96,7 +99,8 @@ class KernelDocDirective(Directive):
if p.returncode != 0:
sys.stderr.write(err)
- env.app.warn('kernel-doc \'%s\' failed with return code %d' % (" ".join(cmd), p.returncode))
+ kernellog.warn(env.app,
+ 'kernel-doc \'%s\' failed with return code %d' % (" ".join(cmd), p.returncode))
return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))]
elif env.config.kerneldoc_verbosity > 0:
sys.stderr.write(err)
@@ -128,8 +132,8 @@ class KernelDocDirective(Directive):
return node.children
except Exception as e: # pylint: disable=W0703
- env.app.warn('kernel-doc \'%s\' processing failed with: %s' %
- (" ".join(cmd), str(e)))
+ kernellog.warn(env.app, 'kernel-doc \'%s\' processing failed with: %s' %
+ (" ".join(cmd), str(e)))
return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))]
def setup(app):
diff --git a/doc/sphinx/kernellog.py b/doc/sphinx/kernellog.py
new file mode 100644
index 0000000000..af924f51a7
--- /dev/null
+++ b/doc/sphinx/kernellog.py
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Sphinx has deprecated its older logging interface, but the replacement
+# only goes back to 1.6. So here's a wrapper layer to keep around for
+# as long as we support 1.4.
+#
+import sphinx
+
+if sphinx.__version__[:3] >= '1.6':
+ UseLogging = True
+ from sphinx.util import logging
+ logger = logging.getLogger('kerneldoc')
+else:
+ UseLogging = False
+
+def warn(app, message):
+ if UseLogging:
+ logger.warning(message)
+ else:
+ app.warn(message)
+
+def verbose(app, message):
+ if UseLogging:
+ logger.verbose(message)
+ else:
+ app.verbose(message)
+
+
diff --git a/doc/sphinx/kfigure.py b/doc/sphinx/kfigure.py
index b97228d2cc..fbfe6693bb 100644
--- a/doc/sphinx/kfigure.py
+++ b/doc/sphinx/kfigure.py
@@ -60,6 +60,8 @@ import sphinx
from sphinx.util.nodes import clean_astext
from six import iteritems
+import kernellog
+
PY3 = sys.version_info[0] == 3
if PY3:
@@ -171,20 +173,20 @@ def setupTools(app):
This function is called once, when the builder is initiated.
"""
global dot_cmd, convert_cmd # pylint: disable=W0603
- app.verbose("kfigure: check installed tools ...")
+ kernellog.verbose(app, "kfigure: check installed tools ...")
dot_cmd = which('dot')
convert_cmd = which('convert')
if dot_cmd:
- app.verbose("use dot(1) from: " + dot_cmd)
+ kernellog.verbose(app, "use dot(1) from: " + dot_cmd)
else:
- app.warn("dot(1) not found, for better output quality install "
- "graphviz from http://www.graphviz.org")
+ kernellog.warn(app, "dot(1) not found, for better output quality install "
+ "graphviz from http://www.graphviz.org")
if convert_cmd:
- app.verbose("use convert(1) from: " + convert_cmd)
+ kernellog.verbose(app, "use convert(1) from: " + convert_cmd)
else:
- app.warn(
+ kernellog.warn(app,
"convert(1) not found, for SVG to PDF conversion install "
"ImageMagick (https://www.imagemagick.org)")
@@ -220,12 +222,13 @@ def convert_image(img_node, translator, src_fname=None):
# in kernel builds, use 'make SPHINXOPTS=-v' to see verbose messages
- app.verbose('assert best format for: ' + img_node['uri'])
+ kernellog.verbose(app, 'assert best format for: ' + img_node['uri'])
if in_ext == '.dot':
if not dot_cmd:
- app.verbose("dot from graphviz not available / include DOT raw.")
+ kernellog.verbose(app,
+ "dot from graphviz not available / include DOT raw.")
img_node.replace_self(file2literal(src_fname))
elif translator.builder.format == 'latex':
@@ -252,7 +255,8 @@ def convert_image(img_node, translator, src_fname=None):
if translator.builder.format == 'latex':
if convert_cmd is None:
- app.verbose("no SVG to PDF conversion available / include SVG raw.")
+ kernellog.verbose(app,
+ "no SVG to PDF conversion available / include SVG raw.")
img_node.replace_self(file2literal(src_fname))
else:
dst_fname = path.join(translator.builder.outdir, fname + '.pdf')
@@ -265,18 +269,19 @@ def convert_image(img_node, translator, src_fname=None):
_name = dst_fname[len(translator.builder.outdir) + 1:]
if isNewer(dst_fname, src_fname):
- app.verbose("convert: {out}/%s already exists and is newer" % _name)
+ kernellog.verbose(app,
+ "convert: {out}/%s already exists and is newer" % _name)
else:
ok = False
mkdir(path.dirname(dst_fname))
if in_ext == '.dot':
- app.verbose('convert DOT to: {out}/' + _name)
+ kernellog.verbose(app, 'convert DOT to: {out}/' + _name)
ok = dot2format(app, src_fname, dst_fname)
elif in_ext == '.svg':
- app.verbose('convert SVG to: {out}/' + _name)
+ kernellog.verbose(app, 'convert SVG to: {out}/' + _name)
ok = svg2pdf(app, src_fname, dst_fname)
if not ok:
@@ -305,7 +310,8 @@ def dot2format(app, dot_fname, out_fname):
with open(out_fname, "w") as out:
exit_code = subprocess.call(cmd, stdout = out)
if exit_code != 0:
- app.warn("Error #%d when calling: %s" % (exit_code, " ".join(cmd)))
+ kernellog.warn(app,
+ "Error #%d when calling: %s" % (exit_code, " ".join(cmd)))
return bool(exit_code == 0)
def svg2pdf(app, svg_fname, pdf_fname):
@@ -322,7 +328,7 @@ def svg2pdf(app, svg_fname, pdf_fname):
# use stdout and stderr from parent
exit_code = subprocess.call(cmd)
if exit_code != 0:
- app.warn("Error #%d when calling: %s" % (exit_code, " ".join(cmd)))
+ kernellog.warn(app, "Error #%d when calling: %s" % (exit_code, " ".join(cmd)))
return bool(exit_code == 0)
@@ -415,15 +421,15 @@ def visit_kernel_render(self, node):
app = self.builder.app
srclang = node.get('srclang')
- app.verbose('visit kernel-render node lang: "%s"' % (srclang))
+ kernellog.verbose(app, 'visit kernel-render node lang: "%s"' % (srclang))
tmp_ext = RENDER_MARKUP_EXT.get(srclang, None)
if tmp_ext is None:
- app.warn('kernel-render: "%s" unknown / include raw.' % (srclang))
+ kernellog.warn(app, 'kernel-render: "%s" unknown / include raw.' % (srclang))
return
if not dot_cmd and tmp_ext == '.dot':
- app.verbose("dot from graphviz not available / include raw.")
+ kernellog.verbose(app, "dot from graphviz not available / include raw.")
return
literal_block = node[0]
diff --git a/doc/uImage.FIT/signature.txt b/doc/uImage.FIT/signature.txt
index c9b1802686..eee06517fa 100644
--- a/doc/uImage.FIT/signature.txt
+++ b/doc/uImage.FIT/signature.txt
@@ -216,7 +216,7 @@ As an example, consider this FIT:
kernel = "kernel-1";
fdt = "fdt-1";
};
- conf-1 {
+ conf-2 {
kernel = "kernel-2";
fdt = "fdt-2";
};
@@ -232,7 +232,7 @@ configuration 3 with kernel 1 and fdt 2:
kernel = "kernel-1";
fdt = "fdt-1";
};
- conf-1 {
+ conf-2 {
kernel = "kernel-2";
fdt = "fdt-2";
};
@@ -337,6 +337,7 @@ WARNING: When relying on signed FIT images with required signature check
the legacy image format is default disabled by not defining
CONFIG_LEGACY_IMAGE_FORMAT
+
Testing
-------
An easy way to test signing and verification is to use the test script
@@ -349,6 +350,8 @@ A sample run is show below:
$ make O=sandbox sandbox_config
$ make O=sandbox
$ O=sandbox ./test/vboot/vboot_test.sh
+
+
Simple Verified Boot Test
=========================
diff --git a/include/android_ab.h b/include/android_ab.h
new file mode 100644
index 0000000000..810906d22b
--- /dev/null
+++ b/include/android_ab.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ */
+
+#ifndef __ANDROID_AB_H
+#define __ANDROID_AB_H
+
+#include <common.h>
+
+/* Android standard boot slot names are 'a', 'b', 'c', ... */
+#define BOOT_SLOT_NAME(slot_num) ('a' + (slot_num))
+
+/* Number of slots */
+#define NUM_SLOTS 2
+
+/**
+ * Select the slot where to boot from.
+ *
+ * On Android devices with more than one boot slot (multiple copies of the
+ * kernel and system images) selects which slot should be used to boot from and
+ * registers the boot attempt. This is used in by the new A/B update model where
+ * one slot is updated in the background while running from the other slot. If
+ * the selected slot did not successfully boot in the past, a boot attempt is
+ * registered before returning from this function so it isn't selected
+ * indefinitely.
+ *
+ * @param[in] dev_desc Place to store the device description pointer
+ * @param[in] part_info Place to store the partition information
+ * @return The slot number (>= 0) on success, or a negative on error
+ */
+int ab_select_slot(struct blk_desc *dev_desc, disk_partition_t *part_info);
+
+#endif /* __ANDROID_AB_H */
diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h
index 26e61ef196..3570a32dff 100644
--- a/include/config_distro_bootcmd.h
+++ b/include/config_distro_bootcmd.h
@@ -254,11 +254,11 @@
#endif
#if defined(CONFIG_DM_PCI)
-#define BOOTENV_RUN_NET_PCI_ENUM "run boot_net_pci_enum; "
+#define BOOTENV_RUN_PCI_ENUM "run boot_pci_enum; "
#define BOOTENV_SHARED_PCI \
- "boot_net_pci_enum=pci enum\0"
+ "boot_pci_enum=pci enum\0"
#else
-#define BOOTENV_RUN_NET_PCI_ENUM
+#define BOOTENV_RUN_PCI_ENUM
#define BOOTENV_SHARED_PCI
#endif
@@ -281,10 +281,24 @@
#endif
#ifdef CONFIG_CMD_VIRTIO
-#define BOOTENV_SHARED_VIRTIO BOOTENV_SHARED_BLKDEV(virtio)
+#define BOOTENV_RUN_VIRTIO_INIT "run virtio_init; "
+#define BOOTENV_SET_VIRTIO_NEED_INIT "virtio_need_init=; "
+#define BOOTENV_SHARED_VIRTIO \
+ "virtio_init=" \
+ "if ${virtio_need_init}; then " \
+ "virtio_need_init=false; " \
+ "virtio scan; " \
+ "fi\0" \
+ \
+ "virtio_boot=" \
+ BOOTENV_RUN_PCI_ENUM \
+ BOOTENV_RUN_VIRTIO_INIT \
+ BOOTENV_SHARED_BLKDEV_BODY(virtio)
#define BOOTENV_DEV_VIRTIO BOOTENV_DEV_BLKDEV
#define BOOTENV_DEV_NAME_VIRTIO BOOTENV_DEV_NAME_BLKDEV
#else
+#define BOOTENV_RUN_VIRTIO_INIT
+#define BOOTENV_SET_VIRTIO_NEED_INIT
#define BOOTENV_SHARED_VIRTIO
#define BOOTENV_DEV_VIRTIO \
BOOT_TARGET_DEVICES_references_VIRTIO_without_CONFIG_CMD_VIRTIO
@@ -350,7 +364,7 @@
#define BOOTENV_DEV_DHCP(devtypeu, devtypel, instance) \
"bootcmd_dhcp=" \
BOOTENV_RUN_NET_USB_START \
- BOOTENV_RUN_NET_PCI_ENUM \
+ BOOTENV_RUN_PCI_ENUM \
"if dhcp ${scriptaddr} ${boot_script_dhcp}; then " \
"source ${scriptaddr}; " \
"fi;" \
@@ -369,7 +383,7 @@
#define BOOTENV_DEV_PXE(devtypeu, devtypel, instance) \
"bootcmd_pxe=" \
BOOTENV_RUN_NET_USB_START \
- BOOTENV_RUN_NET_PCI_ENUM \
+ BOOTENV_RUN_PCI_ENUM \
"dhcp; " \
"if pxe get; then " \
"pxe boot; " \
@@ -465,6 +479,7 @@
"distro_bootcmd=" BOOTENV_SET_SCSI_NEED_INIT \
BOOTENV_SET_NVME_NEED_INIT \
BOOTENV_SET_IDE_NEED_INIT \
+ BOOTENV_SET_VIRTIO_NEED_INIT \
"for target in ${boot_targets}; do " \
"run bootcmd_${target}; " \
"done\0"
diff --git a/include/environment/ti/boot.h b/include/environment/ti/boot.h
index 05bdbbc23e..54e9b2de4d 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=20M,uuid=${uuid_gpt_boot_a};" \
+ "name=boot_b,size=20M,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=20M,uuid=${uuid_gpt_boot};" \
+ "name=system,size=1024M,uuid=${uuid_gpt_system};"
+#endif
+
#ifndef PARTS_DEFAULT
/* Define the default GPT table for eMMC */
#define PARTS_DEFAULT \
@@ -38,8 +50,7 @@
"name=uboot-env,start=2432K,size=256K,uuid=${uuid_gpt_reserved};" \
"name=misc,size=128K,uuid=${uuid_gpt_misc};" \
"name=recovery,size=40M,uuid=${uuid_gpt_recovery};" \
- "name=boot,size=10M,uuid=${uuid_gpt_boot};" \
- "name=system,size=1024M,uuid=${uuid_gpt_system};" \
+ COMMON_PARTS \
"name=vendor,size=256M,uuid=${uuid_gpt_vendor};" \
VBMETA_PART \
"name=userdata,size=-,uuid=${uuid_gpt_userdata}"
@@ -58,6 +69,35 @@
#define AVB_VERIFY_CMD ""
#endif
+#define CONTROL_PARTITION "misc"
+
+#if defined(CONFIG_CMD_AB_SELECT)
+#define AB_SELECT \
+ "if part number mmc 1 " CONTROL_PARTITION " control_part_number; " \
+ "then " \
+ "echo " CONTROL_PARTITION \
+ " partition number:${control_part_number};" \
+ "ab_select slot_name mmc ${mmcdev}:${control_part_number};" \
+ "else " \
+ "echo " CONTROL_PARTITION " partition not found;" \
+ "exit;" \
+ "fi;" \
+ "setenv slot_suffix _${slot_name};" \
+ "if part number mmc ${mmcdev} system${slot_suffix} " \
+ "system_part_number; then " \
+ "setenv bootargs_ab " \
+ "ro root=/dev/mmcblk${mmcdev}p${system_part_number} " \
+ "rootwait init=/init skip_initramfs " \
+ "androidboot.slot_suffix=${slot_suffix};" \
+ "echo A/B cmdline addition: ${bootargs_ab};" \
+ "setenv bootargs ${bootargs} ${bootargs_ab};" \
+ "else " \
+ "echo system${slot_suffix} partition not found;" \
+ "fi;"
+#else
+#define AB_SELECT ""
+#endif
+
#define DEFAULT_COMMON_BOOT_TI_ARGS \
"console=" CONSOLEDEV ",115200n8\0" \
"fdtfile=undefined\0" \
@@ -86,10 +126,16 @@
"mmc dev $mmcdev; " \
"mmc rescan; " \
AVB_VERIFY_CHECK \
- "part start mmc ${mmcdev} boot boot_start; " \
- "part size mmc ${mmcdev} boot boot_size; " \
- "mmc read ${loadaddr} ${boot_start} ${boot_size}; " \
- "bootm ${loadaddr}#${fdtfile};\0 "
+ AB_SELECT \
+ "if part start mmc ${mmcdev} boot${slot_suffix} boot_start; " \
+ "then " \
+ "part size mmc ${mmcdev} boot${slot_suffix} " \
+ "boot_size; " \
+ "mmc read ${loadaddr} ${boot_start} ${boot_size}; " \
+ "bootm ${loadaddr}#${fdtfile}; " \
+ "else " \
+ "echo boot${slot_suffix} partition not found; " \
+ "fi;\0"
#ifdef CONFIG_OMAP54XX
diff --git a/include/part.h b/include/part.h
index ebca546db5..0b5cf3d5e8 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
diff --git a/include/remoteproc.h b/include/remoteproc.h
index c29c0867bc..4987194905 100644
--- a/include/remoteproc.h
+++ b/include/remoteproc.h
@@ -130,7 +130,7 @@ struct dm_rproc_ops {
/* Accessor */
#define rproc_get_ops(dev) ((struct dm_rproc_ops *)(dev)->driver->ops)
-#ifdef CONFIG_REMOTEPROC
+#if CONFIG_IS_ENABLED(REMOTEPROC)
/**
* rproc_init() - Initialize all bound remote proc devices
* @return 0 if all ok, else appropriate error value.
diff --git a/scripts/kernel-doc b/scripts/kernel-doc
index 3cb6259182..516cf1db32 100755
--- a/scripts/kernel-doc
+++ b/scripts/kernel-doc
@@ -212,7 +212,7 @@ my $anon_struct_union = 0;
my $type_constant = '\b``([^\`]+)``\b';
my $type_constant2 = '\%([-_\w]+)';
my $type_func = '(\w+)\(\)';
-my $type_param = '\@(\w*(\.\w+)*(\.\.\.)?)';
+my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params
my $type_env = '(\$\w+)';
my $type_enum = '\&(enum\s*([_\w]+))';
@@ -1062,7 +1062,7 @@ sub dump_struct($$) {
my $x = shift;
my $file = shift;
- if ($x =~ /(struct|union)\s+(\w+)\s*{(.*)}/) {
+ if ($x =~ /(struct|union)\s+(\w+)\s*\{(.*)\}(\s*(__packed|__aligned|__attribute__\s*\(\([a-z0-9,_\s\(\)]*\)\)))*/) {
my $decl_type = $1;
$declaration_name = $2;
my $members = $3;
@@ -1073,8 +1073,9 @@ sub dump_struct($$) {
# strip comments:
$members =~ s/\/\*.*?\*\///gos;
# strip attributes
- $members =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i;
- $members =~ s/__aligned\s*\([^;]*\)//gos;
+ $members =~ s/\s*__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)//gi;
+ $members =~ s/\s*__aligned\s*\([^;]*\)//gos;
+ $members =~ s/\s*__packed\s*//gos;
$members =~ s/\s*CRYPTO_MINALIGN_ATTR//gos;
# replace DECLARE_BITMAP
$members =~ s/DECLARE_BITMAP\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos;
@@ -1148,20 +1149,20 @@ sub dump_struct($$) {
}
}
}
- $members =~ s/(struct|union)([^\{\};]+)\{([^\{\}]*)}([^\{\}\;]*)\;/$newmember/;
+ $members =~ s/(struct|union)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;/$newmember/;
}
# Ignore other nested elements, like enums
- $members =~ s/({[^\{\}]*})//g;
+ $members =~ s/(\{[^\{\}]*\})//g;
create_parameterlist($members, ';', $file, $declaration_name);
check_sections($file, $declaration_name, $decl_type, $sectcheck, $struct_actual);
# Adjust declaration for better display
- $declaration =~ s/([{;])/$1\n/g;
- $declaration =~ s/}\s+;/};/g;
+ $declaration =~ s/([\{;])/$1\n/g;
+ $declaration =~ s/\}\s+;/};/g;
# Better handle inlined enums
- do {} while ($declaration =~ s/(enum\s+{[^}]+),([^\n])/$1,\n$2/);
+ do {} while ($declaration =~ s/(enum\s+\{[^\}]+),([^\n])/$1,\n$2/);
my @def_args = split /\n/, $declaration;
my $level = 1;
@@ -1171,12 +1172,12 @@ sub dump_struct($$) {
$clause =~ s/\s+$//;
$clause =~ s/\s+/ /;
next if (!$clause);
- $level-- if ($clause =~ m/(})/ && $level > 1);
+ $level-- if ($clause =~ m/(\})/ && $level > 1);
if (!($clause =~ m/^\s*#/)) {
$declaration .= "\t" x $level;
}
$declaration .= "\t" . $clause . "\n";
- $level++ if ($clause =~ m/({)/ && !($clause =~m/}/));
+ $level++ if ($clause =~ m/(\{)/ && !($clause =~m/\}/));
}
output_declaration($declaration_name,
'struct',
@@ -1244,7 +1245,7 @@ sub dump_enum($$) {
# strip #define macros inside enums
$x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos;
- if ($x =~ /enum\s+(\w+)\s*{(.*)}/) {
+ if ($x =~ /enum\s+(\w+)\s*\{(.*)\}/) {
$declaration_name = $1;
my $members = $2;
my %_members;
@@ -1381,7 +1382,7 @@ sub create_parameterlist($$$$) {
} elsif ($arg =~ m/\(.+\)\s*\(/) {
# pointer-to-function
$arg =~ tr/#/,/;
- $arg =~ m/[^\(]+\(\*?\s*([\w\.]*)\s*\)/;
+ $arg =~ m/[^\(]+\([\w\s]*\*?\s*([\w\.]*)\s*\)/;
$param = $1;
$type = $arg;
$type =~ s/([^\(]+\(\*?)\s*$param/$1/;
@@ -1473,7 +1474,7 @@ sub push_parameter($$$$) {
if (!defined $parameterdescs{$param} && $param !~ /^#/) {
$parameterdescs{$param} = $undescribed;
- if (show_warnings($type, $declaration_name)) {
+ if (show_warnings($type, $declaration_name) && $param !~ /\./) {
print STDERR
"${file}:$.: warning: Function parameter or member '$param' not described in '$declaration_name'\n";
++$warnings;
@@ -1785,7 +1786,7 @@ sub process_proto_type($$) {
}
while (1) {
- if ( $x =~ /([^{};]*)([{};])(.*)/ ) {
+ if ( $x =~ /([^\{\};]*)([\{\};])(.*)/ ) {
if( length $prototype ) {
$prototype .= " "
}
@@ -1904,13 +1905,13 @@ sub process_name($$) {
++$warnings;
}
- if ($identifier =~ m/^struct/) {
+ if ($identifier =~ m/^struct\b/) {
$decl_type = 'struct';
- } elsif ($identifier =~ m/^union/) {
+ } elsif ($identifier =~ m/^union\b/) {
$decl_type = 'union';
- } elsif ($identifier =~ m/^enum/) {
+ } elsif ($identifier =~ m/^enum\b/) {
$decl_type = 'enum';
- } elsif ($identifier =~ m/^typedef/) {
+ } elsif ($identifier =~ m/^typedef\b/) {
$decl_type = 'typedef';
} else {
$decl_type = 'function';
diff --git a/test/py/tests/test_android/test_ab.py b/test/py/tests/test_android/test_ab.py
new file mode 100644
index 0000000000..c79cb07fda
--- /dev/null
+++ b/test/py/tests/test_android/test_ab.py
@@ -0,0 +1,75 @@
+# 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', '--change-name=1:misc',
+ persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ cmd = ('sgdisk', '--load-backup=' + persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+ cmd = ('cp', persistent, self.path)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+di = None
+@pytest.fixture(scope='function')
+def ab_disk_image(u_boot_console):
+ global di
+ if not di:
+ di = ABTestDiskImage(u_boot_console)
+ return di
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('android_ab')
+@pytest.mark.buildconfigspec('cmd_ab_select')
+@pytest.mark.requiredtool('sgdisk')
+def test_ab(ab_disk_image, u_boot_console):
+ """Test the 'ab_select' command."""
+
+ u_boot_console.run_command('host bind 0 ' + ab_disk_image.path)
+
+ output = u_boot_console.run_command('ab_select slot_name host 0#misc')
+ assert 're-initializing A/B metadata' in output
+ assert 'Attempting slot a, tries remaining 7' in output
+ output = u_boot_console.run_command('printenv slot_name')
+ assert 'slot_name=a' in output
+
+ output = u_boot_console.run_command('ab_select slot_name host 0:1')
+ assert 'Attempting slot b, tries remaining 7' in output
+ output = u_boot_console.run_command('printenv slot_name')
+ assert 'slot_name=b' in output
diff --git a/test/py/tests/test_avb.py b/test/py/tests/test_avb.py
index 2bb75ed6e2..8132423435 100644
--- a/test/py/tests/test_avb.py
+++ b/test/py/tests/test_avb.py
@@ -8,7 +8,7 @@
This tests Android Verified Boot 2.0 support in U-boot:
For additional details about how to build proper vbmeta partition
-check doc/README.avb2
+check doc/android/avb2.txt
For configuration verification:
- Corrupt boot partition and check for failure