Merge branch '2019-07-24-master-imports'
authorTom Rini <trini@konsulko.com>
Wed, 24 Jul 2019 18:15:51 +0000 (14:15 -0400)
committerTom Rini <trini@konsulko.com>
Wed, 24 Jul 2019 18:15:51 +0000 (14:15 -0400)
- Various Android related changes including A/B update and BCB updates
- Assorted minor fixes

35 files changed:
Documentation/.gitignore
arch/arm/cpu/armv7/sunxi/psci.c
arch/arm/include/asm/system.h
arch/arm/mach-imx/mx7/psci-mx7.c
arch/arm/mach-stm32mp/psci.c
arch/arm/mach-uniphier/arm32/psci.c
cmd/Kconfig
cmd/Makefile
cmd/ab_select.c [new file with mode: 0644]
cmd/bcb.c
cmd/part.c
common/Kconfig
common/Makefile
common/android_ab.c [new file with mode: 0644]
common/image-android.c
common/menu.c
common/spl/Kconfig
common/spl/spl_sata.c
configs/sandbox_defconfig
disk/part.c
doc/.gitignore [new file with mode: 0644]
doc/android/ab.txt [new file with mode: 0644]
doc/conf.py
doc/sphinx/kerneldoc.py
doc/sphinx/kernellog.py [new file with mode: 0644]
doc/sphinx/kfigure.py
doc/uImage.FIT/signature.txt
include/android_ab.h [new file with mode: 0644]
include/config_distro_bootcmd.h
include/environment/ti/boot.h
include/part.h
include/remoteproc.h
scripts/kernel-doc
test/py/tests/test_android/test_ab.py [new file with mode: 0644]
test/py/tests/test_avb.py

index e74fec8693b29ce52094c0f5a055200db18e8570..0d20b6487c61e7d1bde93acf4a14b7a89083a16d 100644 (file)
@@ -1,2 +1 @@
-output
 *.pyc
index f3e8f99a7192c92678af8a3ba7446a19836dc461..2c5d99e9acda4bab5c29ad680299106a28a6b571 100644 (file)
@@ -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();
 
index aed2e3c51ef4b1a66984794264f7de58af707768..a1a5e35ef6fdc847e19f3faec57a61ef705b95e6 100644 (file)
@@ -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)
index 34ba0a9307db02a08f5c7224f92dbfd527b07bee..c98d2e96af52cb15cd11bc0a2863d77bcec61df2 100644 (file)
@@ -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;
index 139bb09292263c5e8db9fb7e40a1d63ed607e3c5..1d91b2d324a8dd7a699f58ecaf1a9e87a6de87da 100644 (file)
@@ -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
index 3f67edf26eb2e2e1f3346777cac71944864f4ced..ef35923f6ac5d872e35229e4af70c1a624dc8e3a 100644 (file)
@@ -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);
 }
index 175c6ad9e3338d0b83062eae932c3e0cd8afc812..9e66cc110de2a7bf07bd0eb8605326bdf1ddd262 100644 (file)
@@ -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
index 0aa37414533a54563f2375a5e62167a7874fd060..43a6b0ee2162172e9a81a6e603a3623376328871 100644 (file)
@@ -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 (file)
index 0000000..7c8f2ee
--- /dev/null
@@ -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"
+);
index 2bd5a744deb55a7b6f83c0dcc5dde57d48ba60ca..9626f2c69e345c984c83a730fdae1ba3bd11024d 100644 (file)
--- 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
                 */
index bfb6488b0f2b9eb602149181a37291c54370e6da..653e13ced17c4f932a685a42a30e48b48f479615 100644 (file)
@@ -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"
 );
index 4865a4dfc86d1d2e0e1d12fc9669e348fdb7c90c..b556b59e9f0ec57b701f5c861b2401e049712931 100644 (file)
@@ -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"
index c7e41ef3073f9a3304a1d52f040203f72560c2d0..302d8beaf356aad14f7ebd7f3e9c7eed711d3545 100644 (file)
@@ -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 (file)
index 0000000..05ffc6f
--- /dev/null
@@ -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;
+}
index 8b0f6b3b8babe5b50578324e1dd023ad46a1185d..6c9568a655d74f4e282a2ef29d6fad6d836ae426 100644 (file)
@@ -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;
index 0f0a29ac2ee3b44172622ca29e597516e165c432..7b66d199a9b13054706f5886e19829a85c322d12 100644 (file)
@@ -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);
index 5978fb2934325e474de2bab0f21926c834ce1487..5d6da5db89bc553a429b5c008814c45ab7db2cf0 100644 (file)
@@ -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
index f0af9f38d19f0c216fce0f067838a7fde388db33..e108af0576aa971a1714a9170cc63173ae3d0bdf 100644 (file)
 #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) {
index 61391a7acd4464561759686643d7ed99dcf00bb9..11cc097cd558a64e807cad7ff909a6c1ef5f4ae0 100644 (file)
@@ -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
index f14bc22b6db669179325e0e1a3549d1a6dca34b7..7e842147317194309b5691d0eda004d35545ef1a 100644 (file)
@@ -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 (file)
index 0000000..53752db
--- /dev/null
@@ -0,0 +1 @@
+output
diff --git a/doc/android/ab.txt b/doc/android/ab.txt
new file mode 100644 (file)
index 0000000..9f37ed5
--- /dev/null
@@ -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
index 168c31346b6614242ea560b9f098045b877363c5..0772fb6f0c8991796efc86eddbb10ec1e2b1e3b4 100644 (file)
@@ -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
index fbedcc39460bb28b156db85ac085fbb077d3fe6e..e536360de16406e0e498e96d9a7a3304023291d3 100644 (file)
@@ -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 (file)
index 0000000..af924f5
--- /dev/null
@@ -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)
+
+
index b97228d2cc0eef84c156f06cc1515602ddf4a24d..fbfe6693bb60aa36157c60d33e6a2d0c1c630154 100644 (file)
@@ -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]
index c9b1802686f4a6ff83861d9342d4980a357b47b8..eee06517fa5ecbaa584abd440e6dd9212001ff3a 100644 (file)
@@ -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 (file)
index 0000000..810906d
--- /dev/null
@@ -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 */
index 26e61ef196906411eb1ccbceff398906a46fad76..3570a32dff86b0eec462b7eaab4d54e78adde58d 100644 (file)
 #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
 
 #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
 #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;" \
 #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; " \
        "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"
index 05bdbbc23e47ecdc7c623bbb1a58fe2a3b757758..54e9b2de4d1296eed772e076bfbfcdebe8bd050f 100644 (file)
 #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}"
 #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" \
                "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
 
index ebca546db5d36d07e2b62a06a22adb1ee81a41a1..0b5cf3d5e813a3f4b35a52804bf7bdc74eb5a2b1 100644 (file)
@@ -201,6 +201,27 @@ int part_get_info_by_name_type(struct blk_desc *dev_desc, const char *name,
 int part_get_info_by_name(struct blk_desc *dev_desc,
                              const char *name, disk_partition_t *info);
 
+/**
+ * Get partition info from dev number + part name, or dev number + part number.
+ *
+ * Parse a device number and partition description (either name or number)
+ * in the form of device number plus partition name separated by a "#"
+ * (like "device_num#partition_name") or a device number plus a partition number
+ * separated by a ":". For example both "0#misc" and "0:1" can be valid
+ * partition descriptions for a given interface. If the partition is found, sets
+ * dev_desc and part_info accordingly with the information of the partition.
+ *
+ * @param[in] dev_iface        Device interface
+ * @param[in] dev_part_str Input partition description, like "0#misc" or "0:1"
+ * @param[out] dev_desc        Place to store the device description pointer
+ * @param[out] part_info Place to store the partition information
+ * @return 0 on success, or a negative on error
+ */
+int part_get_info_by_dev_and_name_or_num(const char *dev_iface,
+                                        const char *dev_part_str,
+                                        struct blk_desc **dev_desc,
+                                        disk_partition_t *part_info);
+
 /**
  * part_set_generic_name() - create generic partition like hda1 or sdb2
  *
index c29c0867bcf21942224b1bf5ff3739c00f5243cf..49871949055a220eb924f96df2593827e003d3f5 100644 (file)
@@ -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.
index 3cb6259182fa8eb7f2edc6e2f6a569184004bda6..516cf1db32c1fc2cb0958eb01337399dab61ed8d 100755 (executable)
@@ -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 (file)
index 0000000..c79cb07
--- /dev/null
@@ -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
index 2bb75ed6e2a2f8d271a7af08850c2546e35f857e..8132423435550bb94867a6dbe7e0818b8772edf4 100644 (file)
@@ -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