Merge branch '2019-07-24-master-imports'

- Various Android related changes including A/B update and BCB updates
- Assorted minor fixes
This commit is contained in:
Tom Rini 2019-07-24 14:15:51 -04:00
commit a9aa4c5700
35 changed files with 954 additions and 114 deletions

View file

@ -1,2 +1 @@
output
*.pyc

View 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();

View 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)

View 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;

View 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

View 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);
}

View 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

View 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

52
cmd/ab_select.c Normal file
View file

@ -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"
);

View file

@ -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
*/

View 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"
);

View 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"

View 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

300
common/android_ab.c Normal file
View file

@ -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;
}

View 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;

View 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);

View 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

View file

@ -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) {

View 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

View 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)
{

1
doc/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
output

67
doc/android/ab.txt Normal file
View file

@ -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

View 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

View 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):

28
doc/sphinx/kernellog.py Normal file
View file

@ -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)

View 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]

View 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
=========================

34
include/android_ab.h Normal file
View file

@ -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 */

View file

@ -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"

View file

@ -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

View 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
*

View 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.

View file

@ -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';

View file

@ -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

View 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