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 *.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; return ARM_PSCI_RET_SUCCESS;
} }
void __secure psci_cpu_off(void) s32 __secure psci_cpu_off(void)
{ {
psci_cpu_off_common(); psci_cpu_off_common();

View File

@ -516,6 +516,21 @@ enum {
*/ */
void mmu_page_table_flush(unsigned long start, unsigned long stop); 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__ */ #endif /* __ASSEMBLY__ */
#define arch_align_stack(x) (x) #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]; 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 */ /* Trusted OS is either not present or does not require migration */
return 2; return 2;

View File

@ -30,7 +30,7 @@ u8 psci_state[STM32MP1_PSCI_NR_CPUS] __secure_data = {
PSCI_AFFINITY_LEVEL_ON, PSCI_AFFINITY_LEVEL_ON,
PSCI_AFFINITY_LEVEL_OFF}; 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; psci_state[cpu] = state;
dsb(); dsb();
@ -67,7 +67,7 @@ void __secure psci_arch_cpu_entry(void)
writel(0xFFFFFFFF, TAMP_BACKUP_MAGIC_NUMBER); 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) { switch (psci_fid) {
case ARM_PSCI_0_2_FN_PSCI_VERSION: 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; return ARM_PSCI_RET_NI;
} }
unsigned int __secure psci_version(u32 function_id) u32 __secure psci_version(void)
{ {
return ARM_PSCI_VER_1_0; 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 lowest_affinity_level)
{ {
u32 cpu = target_affinity & MPIDR_AFF0; 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]; 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 * 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; 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 context_id)
{ {
u32 cpu = target_cpu & MPIDR_AFF0; 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; return ARM_PSCI_RET_SUCCESS;
} }
int __secure psci_cpu_off(u32 function_id) s32 __secure psci_cpu_off(void)
{ {
u32 cpu; u32 cpu;
@ -181,7 +181,7 @@ int __secure psci_cpu_off(u32 function_id)
wfi(); wfi();
} }
void __secure psci_system_reset(u32 function_id) void __secure psci_system_reset(void)
{ {
/* System reset */ /* System reset */
writel(RCC_MP_GRSTCSETR_MPSYSRST, RCC_MP_GRSTCSETR); writel(RCC_MP_GRSTCSETR_MPSYSRST, RCC_MP_GRSTCSETR);
@ -190,7 +190,7 @@ void __secure psci_system_reset(u32 function_id)
wfi(); wfi();
} }
void __secure psci_system_off(u32 function_id) void __secure psci_system_off(void)
{ {
/* System Off is not managed, waiting user power off /* System Off is not managed, waiting user power off
* TODO: handle I2C write in PMIC Main Control register bit 0 = SWOFF * 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; 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 context_id)
{ {
u32 cpu = cpuid & 0xff; 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; return PSCI_RET_SUCCESS;
} }
void __secure psci_system_reset(u32 function_id) void __secure psci_system_reset(void)
{ {
reset_cpu(0); reset_cpu(0);
} }

View File

@ -735,7 +735,7 @@ config CMD_FASTBOOT
Android devices. Fastboot requires either the network stack Android devices. Fastboot requires either the network stack
enabled or support for acting as a USB device. 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 config CMD_FDC
bool "fdcboot - Boot from floppy device" bool "fdcboot - Boot from floppy device"
@ -1198,6 +1198,21 @@ config CMD_SETEXPR
endmenu 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 if NET
menuconfig CMD_NET menuconfig CMD_NET

View File

@ -12,6 +12,7 @@ obj-y += version.o
# command # command
obj-$(CONFIG_CMD_AES) += aes.o obj-$(CONFIG_CMD_AES) += aes.o
obj-$(CONFIG_CMD_AB_SELECT) += ab_select.o
obj-$(CONFIG_CMD_ADC) += adc.o obj-$(CONFIG_CMD_ADC) += adc.o
obj-$(CONFIG_CMD_ARMFLASH) += armflash.o obj-$(CONFIG_CMD_ARMFLASH) += armflash.o
obj-y += blk_common.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) static int bcb_cmd_get(char *cmd)
{ {
if (!strncmp(cmd, "load", sizeof("load"))) if (!strcmp(cmd, "load"))
return BCB_CMD_LOAD; return BCB_CMD_LOAD;
if (!strncmp(cmd, "set", sizeof("set"))) if (!strcmp(cmd, "set"))
return BCB_CMD_FIELD_SET; return BCB_CMD_FIELD_SET;
if (!strncmp(cmd, "clear", sizeof("clear"))) if (!strcmp(cmd, "clear"))
return BCB_CMD_FIELD_CLEAR; return BCB_CMD_FIELD_CLEAR;
if (!strncmp(cmd, "test", sizeof("test"))) if (!strcmp(cmd, "test"))
return BCB_CMD_FIELD_TEST; return BCB_CMD_FIELD_TEST;
if (!strncmp(cmd, "store", sizeof("store"))) if (!strcmp(cmd, "store"))
return BCB_CMD_STORE; return BCB_CMD_STORE;
if (!strncmp(cmd, "dump", sizeof("dump"))) if (!strcmp(cmd, "dump"))
return BCB_CMD_FIELD_DUMP; return BCB_CMD_FIELD_DUMP;
else else
return -1; return -1;
@ -46,9 +46,6 @@ static int bcb_is_misused(int argc, char *const argv[])
switch (cmd) { switch (cmd) {
case BCB_CMD_LOAD: case BCB_CMD_LOAD:
if (argc != 3)
goto err;
break;
case BCB_CMD_FIELD_SET: case BCB_CMD_FIELD_SET:
if (argc != 3) if (argc != 3)
goto err; goto err;
@ -86,23 +83,23 @@ err:
return -1; 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"))) { if (!strcmp(name, "command")) {
*field = bcb.command; *fieldp = bcb.command;
*size = sizeof(bcb.command); *sizep = sizeof(bcb.command);
} else if (!strncmp(name, "status", sizeof("status"))) { } else if (!strcmp(name, "status")) {
*field = bcb.status; *fieldp = bcb.status;
*size = sizeof(bcb.status); *sizep = sizeof(bcb.status);
} else if (!strncmp(name, "recovery", sizeof("recovery"))) { } else if (!strcmp(name, "recovery")) {
*field = bcb.recovery; *fieldp = bcb.recovery;
*size = sizeof(bcb.recovery); *sizep = sizeof(bcb.recovery);
} else if (!strncmp(name, "stage", sizeof("stage"))) { } else if (!strcmp(name, "stage")) {
*field = bcb.stage; *fieldp = bcb.stage;
*size = sizeof(bcb.stage); *sizep = sizeof(bcb.stage);
} else if (!strncmp(name, "reserved", sizeof("reserved"))) { } else if (!strcmp(name, "reserved")) {
*field = bcb.reserved; *fieldp = bcb.reserved;
*size = sizeof(bcb.reserved); *sizep = sizeof(bcb.reserved);
} else { } else {
printf("Error: Unknown bcb field '%s'\n", name); printf("Error: Unknown bcb field '%s'\n", name);
return -1; return -1;
@ -111,8 +108,8 @@ static int bcb_field_get(char *name, char **field, int *size)
return 0; return 0;
} }
static int static int do_bcb_load(cmd_tbl_t *cmdtp, int flag, int argc,
do_bcb_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) char * const argv[])
{ {
struct blk_desc *desc; struct blk_desc *desc;
disk_partition_t info; 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); ret = blk_get_device_by_str("mmc", argv[1], &desc);
if (ret < 0) if (ret < 0)
goto err_1; goto err_read_fail;
part = simple_strtoul(argv[2], &endp, 0); part = simple_strtoul(argv[2], &endp, 0);
if (*endp == '\0') { if (*endp == '\0') {
ret = part_get_info(desc, part, &info); ret = part_get_info(desc, part, &info);
if (ret) if (ret)
goto err_1; goto err_read_fail;
} else { } else {
part = part_get_info_by_name(desc, argv[2], &info); part = part_get_info_by_name(desc, argv[2], &info);
if (part < 0) { if (part < 0) {
ret = part; ret = part;
goto err_1; goto err_read_fail;
} }
} }
cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), info.blksz); cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), info.blksz);
if (cnt > info.size) if (cnt > info.size)
goto err_2; goto err_too_small;
if (blk_dread(desc, info.start, cnt, &bcb) != cnt) { if (blk_dread(desc, info.start, cnt, &bcb) != cnt) {
ret = -EIO; ret = -EIO;
goto err_1; goto err_read_fail;
} }
bcb_dev = desc->devnum; 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); debug("%s: Loaded from mmc %d:%d\n", __func__, bcb_dev, bcb_part);
return CMD_RET_SUCCESS; return CMD_RET_SUCCESS;
err_1: err_read_fail:
printf("Error: mmc %s:%s read failed (%d)\n", argv[1], argv[2], ret); printf("Error: mmc %s:%s read failed (%d)\n", argv[1], argv[2], ret);
goto err; goto err;
err_2: err_too_small:
printf("Error: mmc %s:%s too small!", argv[1], argv[2]); printf("Error: mmc %s:%s too small!", argv[1], argv[2]);
goto err; goto err;
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; return CMD_RET_USAGE;
if (bcb_is_misused(argc, argv)) { 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, * root-cause of misusage, so don't return CMD_RET_USAGE,
* since the latter prints out the full-blown help text * since the latter prints out the full-blown help text
*/ */

View File

@ -24,6 +24,7 @@
enum cmd_part_info { enum cmd_part_info {
CMD_PART_INFO_START = 0, CMD_PART_INFO_START = 0,
CMD_PART_INFO_SIZE, CMD_PART_INFO_SIZE,
CMD_PART_INFO_NUMBER
}; };
static int do_part_uuid(int argc, char * const argv[]) 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: case CMD_PART_INFO_SIZE:
snprintf(buf, sizeof(buf), LBAF, info.size); snprintf(buf, sizeof(buf), LBAF, info.size);
break; break;
case CMD_PART_INFO_NUMBER:
snprintf(buf, sizeof(buf), "%d", part);
break;
default: default:
printf("** Unknown cmd_part_info value: %d\n", param); printf("** Unknown cmd_part_info value: %d\n", param);
return 1; 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); 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[]) static int do_part(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{ {
if (argc < 2) 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); return do_part_start(argc - 2, argv + 2);
else if (!strcmp(argv[1], "size")) else if (!strcmp(argv[1], "size"))
return do_part_size(argc - 2, argv + 2); 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; return CMD_RET_USAGE;
} }
@ -206,5 +217,8 @@ U_BOOT_CMD(
" part can be either partition number or partition name\n" " part can be either partition number or partition name\n"
"part size <interface> <dev> <part> <varname>\n" "part size <interface> <dev> <part> <varname>\n"
" - set environment variable to the size of the partition (in blocks)\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 default 100
depends on UPDATE_TFTP 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 endmenu
menu "Blob list" menu "Blob list"

View File

@ -107,6 +107,7 @@ endif
endif endif
obj-y += image.o obj-y += image.o
obj-$(CONFIG_ANDROID_AB) += android_ab.o
obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o
obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o
obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.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) ulong *os_data, ulong *os_len)
{ {
u32 kernel_addr = android_image_get_kernel_addr(hdr); 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 * 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); env_set("bootargs", newbootargs);
if (os_data) { if (os_data) {
*os_data = (ulong)hdr; if (image_get_magic(ihdr) == IH_MAGIC) {
*os_data += hdr->page_size; *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; 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); 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; return IH_COMP_LZ4;
else else
return IH_COMP_NONE; return IH_COMP_NONE;

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+ // SPDX-License-Identifier: GPL-2.0+
/* /*
* Copyright 2010-2011 Calxeda, Inc. * Copyright 2010-2011 Calxeda, Inc.
* Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
*/ */
#include <common.h> #include <common.h>
@ -39,6 +40,7 @@ struct menu {
char *(*item_choice)(void *); char *(*item_choice)(void *);
void *item_choice_data; void *item_choice_data;
struct list_head items; struct list_head items;
int item_cnt;
}; };
/* /*
@ -271,7 +273,7 @@ int menu_get_choice(struct menu *m, void **choice)
if (!m || !choice) if (!m || !choice)
return -EINVAL; return -EINVAL;
if (!m->prompt) if (!m->prompt || m->item_cnt == 1)
return menu_default_choice(m, choice); return menu_default_choice(m, choice);
return menu_interactive_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; item->data = item_data;
list_add_tail(&item->list, &m->items); list_add_tail(&item->list, &m->items);
m->item_cnt++;
return 1; 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_data_print = item_data_print;
m->item_choice = item_choice; m->item_choice = item_choice;
m->item_choice_data = item_choice_data; m->item_choice_data = item_choice_data;
m->item_cnt = 0;
if (title) { if (title) {
m->title = strdup(title); m->title = strdup(title);

View File

@ -918,6 +918,20 @@ config SPL_SATA_SUPPORT
expense and power consumption. This enables loading from SATA expense and power consumption. This enables loading from SATA
using a configured device. 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 config SPL_SERIAL_SUPPORT
bool "Support serial" bool "Support serial"
select SPL_PRINTF select SPL_PRINTF

View File

@ -25,6 +25,37 @@
#define CONFIG_SPL_FS_LOAD_PAYLOAD_NAME "u-boot.img" #define CONFIG_SPL_FS_LOAD_PAYLOAD_NAME "u-boot.img"
#endif #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, static int spl_sata_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev) 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, err = spl_load_image_fat(spl_image, stor_dev,
CONFIG_SYS_SATA_FAT_BOOT_PARTITION, CONFIG_SYS_SATA_FAT_BOOT_PARTITION,
CONFIG_SPL_FS_LOAD_PAYLOAD_NAME); 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) { if (err) {

View File

@ -20,6 +20,7 @@ CONFIG_PRE_CON_BUF_ADDR=0xf0000
CONFIG_LOG_MAX_LEVEL=6 CONFIG_LOG_MAX_LEVEL=6
CONFIG_LOG_ERROR_RETURN=y CONFIG_LOG_ERROR_RETURN=y
CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_DISPLAY_BOARDINFO_LATE=y
CONFIG_ANDROID_AB=y
CONFIG_CMD_CPU=y CONFIG_CMD_CPU=y
CONFIG_CMD_LICENSE=y CONFIG_CMD_LICENSE=y
CONFIG_CMD_BOOTZ=y CONFIG_CMD_BOOTZ=y
@ -47,6 +48,7 @@ CONFIG_CMD_REMOTEPROC=y
CONFIG_CMD_SPI=y CONFIG_CMD_SPI=y
CONFIG_CMD_USB=y CONFIG_CMD_USB=y
CONFIG_CMD_AXI=y CONFIG_CMD_AXI=y
CONFIG_CMD_AB_SELECT=y
CONFIG_CMD_TFTPPUT=y CONFIG_CMD_TFTPPUT=y
CONFIG_CMD_TFTPSRV=y CONFIG_CMD_TFTPSRV=y
CONFIG_CMD_RARP=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); 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, void part_set_generic_name(const struct blk_desc *dev_desc,
int part_num, char *name) 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 # The name of an image file (relative to this directory) to place at the top
# of the sidebar. # 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 # 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 # 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 docutils.parsers.rst import directives, Directive
from sphinx.ext.autodoc import AutodocReporter from sphinx.ext.autodoc import AutodocReporter
import kernellog
__version__ = '1.0' __version__ = '1.0'
class KernelDocDirective(Directive): class KernelDocDirective(Directive):
@ -86,7 +88,8 @@ class KernelDocDirective(Directive):
cmd += [filename] cmd += [filename]
try: 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) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate() out, err = p.communicate()
@ -96,7 +99,8 @@ class KernelDocDirective(Directive):
if p.returncode != 0: if p.returncode != 0:
sys.stderr.write(err) 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"))] return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))]
elif env.config.kerneldoc_verbosity > 0: elif env.config.kerneldoc_verbosity > 0:
sys.stderr.write(err) sys.stderr.write(err)
@ -128,8 +132,8 @@ class KernelDocDirective(Directive):
return node.children return node.children
except Exception as e: # pylint: disable=W0703 except Exception as e: # pylint: disable=W0703
env.app.warn('kernel-doc \'%s\' processing failed with: %s' % kernellog.warn(env.app, 'kernel-doc \'%s\' processing failed with: %s' %
(" ".join(cmd), str(e))) (" ".join(cmd), str(e)))
return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))]
def setup(app): 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 sphinx.util.nodes import clean_astext
from six import iteritems from six import iteritems
import kernellog
PY3 = sys.version_info[0] == 3 PY3 = sys.version_info[0] == 3
if PY3: if PY3:
@ -171,20 +173,20 @@ def setupTools(app):
This function is called once, when the builder is initiated. This function is called once, when the builder is initiated.
""" """
global dot_cmd, convert_cmd # pylint: disable=W0603 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') dot_cmd = which('dot')
convert_cmd = which('convert') convert_cmd = which('convert')
if dot_cmd: if dot_cmd:
app.verbose("use dot(1) from: " + dot_cmd) kernellog.verbose(app, "use dot(1) from: " + dot_cmd)
else: else:
app.warn("dot(1) not found, for better output quality install " kernellog.warn(app, "dot(1) not found, for better output quality install "
"graphviz from http://www.graphviz.org") "graphviz from http://www.graphviz.org")
if convert_cmd: if convert_cmd:
app.verbose("use convert(1) from: " + convert_cmd) kernellog.verbose(app, "use convert(1) from: " + convert_cmd)
else: else:
app.warn( kernellog.warn(app,
"convert(1) not found, for SVG to PDF conversion install " "convert(1) not found, for SVG to PDF conversion install "
"ImageMagick (https://www.imagemagick.org)") "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 # 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 in_ext == '.dot':
if not dot_cmd: 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)) img_node.replace_self(file2literal(src_fname))
elif translator.builder.format == 'latex': elif translator.builder.format == 'latex':
@ -252,7 +255,8 @@ def convert_image(img_node, translator, src_fname=None):
if translator.builder.format == 'latex': if translator.builder.format == 'latex':
if convert_cmd is None: 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)) img_node.replace_self(file2literal(src_fname))
else: else:
dst_fname = path.join(translator.builder.outdir, fname + '.pdf') 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:] _name = dst_fname[len(translator.builder.outdir) + 1:]
if isNewer(dst_fname, src_fname): 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: else:
ok = False ok = False
mkdir(path.dirname(dst_fname)) mkdir(path.dirname(dst_fname))
if in_ext == '.dot': 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) ok = dot2format(app, src_fname, dst_fname)
elif in_ext == '.svg': 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) ok = svg2pdf(app, src_fname, dst_fname)
if not ok: if not ok:
@ -305,7 +310,8 @@ def dot2format(app, dot_fname, out_fname):
with open(out_fname, "w") as out: with open(out_fname, "w") as out:
exit_code = subprocess.call(cmd, stdout = out) exit_code = subprocess.call(cmd, stdout = out)
if exit_code != 0: 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) return bool(exit_code == 0)
def svg2pdf(app, svg_fname, pdf_fname): def svg2pdf(app, svg_fname, pdf_fname):
@ -322,7 +328,7 @@ def svg2pdf(app, svg_fname, pdf_fname):
# use stdout and stderr from parent # use stdout and stderr from parent
exit_code = subprocess.call(cmd) exit_code = subprocess.call(cmd)
if exit_code != 0: 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) return bool(exit_code == 0)
@ -415,15 +421,15 @@ def visit_kernel_render(self, node):
app = self.builder.app app = self.builder.app
srclang = node.get('srclang') 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) tmp_ext = RENDER_MARKUP_EXT.get(srclang, None)
if tmp_ext is 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 return
if not dot_cmd and tmp_ext == '.dot': 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 return
literal_block = node[0] literal_block = node[0]

View File

@ -216,7 +216,7 @@ As an example, consider this FIT:
kernel = "kernel-1"; kernel = "kernel-1";
fdt = "fdt-1"; fdt = "fdt-1";
}; };
conf-1 { conf-2 {
kernel = "kernel-2"; kernel = "kernel-2";
fdt = "fdt-2"; fdt = "fdt-2";
}; };
@ -232,7 +232,7 @@ configuration 3 with kernel 1 and fdt 2:
kernel = "kernel-1"; kernel = "kernel-1";
fdt = "fdt-1"; fdt = "fdt-1";
}; };
conf-1 { conf-2 {
kernel = "kernel-2"; kernel = "kernel-2";
fdt = "fdt-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 the legacy image format is default disabled by not defining
CONFIG_LEGACY_IMAGE_FORMAT CONFIG_LEGACY_IMAGE_FORMAT
Testing Testing
------- -------
An easy way to test signing and verification is to use the test script 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 sandbox_config
$ make O=sandbox $ make O=sandbox
$ O=sandbox ./test/vboot/vboot_test.sh $ O=sandbox ./test/vboot/vboot_test.sh
Simple Verified Boot Test 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 #endif
#if defined(CONFIG_DM_PCI) #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 \ #define BOOTENV_SHARED_PCI \
"boot_net_pci_enum=pci enum\0" "boot_pci_enum=pci enum\0"
#else #else
#define BOOTENV_RUN_NET_PCI_ENUM #define BOOTENV_RUN_PCI_ENUM
#define BOOTENV_SHARED_PCI #define BOOTENV_SHARED_PCI
#endif #endif
@ -281,10 +281,24 @@
#endif #endif
#ifdef CONFIG_CMD_VIRTIO #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_VIRTIO BOOTENV_DEV_BLKDEV
#define BOOTENV_DEV_NAME_VIRTIO BOOTENV_DEV_NAME_BLKDEV #define BOOTENV_DEV_NAME_VIRTIO BOOTENV_DEV_NAME_BLKDEV
#else #else
#define BOOTENV_RUN_VIRTIO_INIT
#define BOOTENV_SET_VIRTIO_NEED_INIT
#define BOOTENV_SHARED_VIRTIO #define BOOTENV_SHARED_VIRTIO
#define BOOTENV_DEV_VIRTIO \ #define BOOTENV_DEV_VIRTIO \
BOOT_TARGET_DEVICES_references_VIRTIO_without_CONFIG_CMD_VIRTIO BOOT_TARGET_DEVICES_references_VIRTIO_without_CONFIG_CMD_VIRTIO
@ -350,7 +364,7 @@
#define BOOTENV_DEV_DHCP(devtypeu, devtypel, instance) \ #define BOOTENV_DEV_DHCP(devtypeu, devtypel, instance) \
"bootcmd_dhcp=" \ "bootcmd_dhcp=" \
BOOTENV_RUN_NET_USB_START \ BOOTENV_RUN_NET_USB_START \
BOOTENV_RUN_NET_PCI_ENUM \ BOOTENV_RUN_PCI_ENUM \
"if dhcp ${scriptaddr} ${boot_script_dhcp}; then " \ "if dhcp ${scriptaddr} ${boot_script_dhcp}; then " \
"source ${scriptaddr}; " \ "source ${scriptaddr}; " \
"fi;" \ "fi;" \
@ -369,7 +383,7 @@
#define BOOTENV_DEV_PXE(devtypeu, devtypel, instance) \ #define BOOTENV_DEV_PXE(devtypeu, devtypel, instance) \
"bootcmd_pxe=" \ "bootcmd_pxe=" \
BOOTENV_RUN_NET_USB_START \ BOOTENV_RUN_NET_USB_START \
BOOTENV_RUN_NET_PCI_ENUM \ BOOTENV_RUN_PCI_ENUM \
"dhcp; " \ "dhcp; " \
"if pxe get; then " \ "if pxe get; then " \
"pxe boot; " \ "pxe boot; " \
@ -465,6 +479,7 @@
"distro_bootcmd=" BOOTENV_SET_SCSI_NEED_INIT \ "distro_bootcmd=" BOOTENV_SET_SCSI_NEED_INIT \
BOOTENV_SET_NVME_NEED_INIT \ BOOTENV_SET_NVME_NEED_INIT \
BOOTENV_SET_IDE_NEED_INIT \ BOOTENV_SET_IDE_NEED_INIT \
BOOTENV_SET_VIRTIO_NEED_INIT \
"for target in ${boot_targets}; do " \ "for target in ${boot_targets}; do " \
"run bootcmd_${target}; " \ "run bootcmd_${target}; " \
"done\0" "done\0"

View File

@ -23,6 +23,18 @@
#define VBMETA_PART "" #define VBMETA_PART ""
#endif #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 #ifndef PARTS_DEFAULT
/* Define the default GPT table for eMMC */ /* Define the default GPT table for eMMC */
#define PARTS_DEFAULT \ #define PARTS_DEFAULT \
@ -38,8 +50,7 @@
"name=uboot-env,start=2432K,size=256K,uuid=${uuid_gpt_reserved};" \ "name=uboot-env,start=2432K,size=256K,uuid=${uuid_gpt_reserved};" \
"name=misc,size=128K,uuid=${uuid_gpt_misc};" \ "name=misc,size=128K,uuid=${uuid_gpt_misc};" \
"name=recovery,size=40M,uuid=${uuid_gpt_recovery};" \ "name=recovery,size=40M,uuid=${uuid_gpt_recovery};" \
"name=boot,size=10M,uuid=${uuid_gpt_boot};" \ COMMON_PARTS \
"name=system,size=1024M,uuid=${uuid_gpt_system};" \
"name=vendor,size=256M,uuid=${uuid_gpt_vendor};" \ "name=vendor,size=256M,uuid=${uuid_gpt_vendor};" \
VBMETA_PART \ VBMETA_PART \
"name=userdata,size=-,uuid=${uuid_gpt_userdata}" "name=userdata,size=-,uuid=${uuid_gpt_userdata}"
@ -58,6 +69,35 @@
#define AVB_VERIFY_CMD "" #define AVB_VERIFY_CMD ""
#endif #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 \ #define DEFAULT_COMMON_BOOT_TI_ARGS \
"console=" CONSOLEDEV ",115200n8\0" \ "console=" CONSOLEDEV ",115200n8\0" \
"fdtfile=undefined\0" \ "fdtfile=undefined\0" \
@ -86,10 +126,16 @@
"mmc dev $mmcdev; " \ "mmc dev $mmcdev; " \
"mmc rescan; " \ "mmc rescan; " \
AVB_VERIFY_CHECK \ AVB_VERIFY_CHECK \
"part start mmc ${mmcdev} boot boot_start; " \ AB_SELECT \
"part size mmc ${mmcdev} boot boot_size; " \ "if part start mmc ${mmcdev} boot${slot_suffix} boot_start; " \
"mmc read ${loadaddr} ${boot_start} ${boot_size}; " \ "then " \
"bootm ${loadaddr}#${fdtfile};\0 " "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 #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, int part_get_info_by_name(struct blk_desc *dev_desc,
const char *name, disk_partition_t *info); 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 * part_set_generic_name() - create generic partition like hda1 or sdb2
* *

View File

@ -130,7 +130,7 @@ struct dm_rproc_ops {
/* Accessor */ /* Accessor */
#define rproc_get_ops(dev) ((struct dm_rproc_ops *)(dev)->driver->ops) #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 * rproc_init() - Initialize all bound remote proc devices
* @return 0 if all ok, else appropriate error value. * @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_constant = '\b``([^\`]+)``\b';
my $type_constant2 = '\%([-_\w]+)'; my $type_constant2 = '\%([-_\w]+)';
my $type_func = '(\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_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params
my $type_env = '(\$\w+)'; my $type_env = '(\$\w+)';
my $type_enum = '\&(enum\s*([_\w]+))'; my $type_enum = '\&(enum\s*([_\w]+))';
@ -1062,7 +1062,7 @@ sub dump_struct($$) {
my $x = shift; my $x = shift;
my $file = 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; my $decl_type = $1;
$declaration_name = $2; $declaration_name = $2;
my $members = $3; my $members = $3;
@ -1073,8 +1073,9 @@ sub dump_struct($$) {
# strip comments: # strip comments:
$members =~ s/\/\*.*?\*\///gos; $members =~ s/\/\*.*?\*\///gos;
# strip attributes # strip attributes
$members =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i; $members =~ s/\s*__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)//gi;
$members =~ s/__aligned\s*\([^;]*\)//gos; $members =~ s/\s*__aligned\s*\([^;]*\)//gos;
$members =~ s/\s*__packed\s*//gos;
$members =~ s/\s*CRYPTO_MINALIGN_ATTR//gos; $members =~ s/\s*CRYPTO_MINALIGN_ATTR//gos;
# replace DECLARE_BITMAP # replace DECLARE_BITMAP
$members =~ s/DECLARE_BITMAP\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos; $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 # Ignore other nested elements, like enums
$members =~ s/({[^\{\}]*})//g; $members =~ s/(\{[^\{\}]*\})//g;
create_parameterlist($members, ';', $file, $declaration_name); create_parameterlist($members, ';', $file, $declaration_name);
check_sections($file, $declaration_name, $decl_type, $sectcheck, $struct_actual); check_sections($file, $declaration_name, $decl_type, $sectcheck, $struct_actual);
# Adjust declaration for better display # Adjust declaration for better display
$declaration =~ s/([{;])/$1\n/g; $declaration =~ s/([\{;])/$1\n/g;
$declaration =~ s/}\s+;/};/g; $declaration =~ s/\}\s+;/};/g;
# Better handle inlined enums # 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 @def_args = split /\n/, $declaration;
my $level = 1; my $level = 1;
@ -1171,12 +1172,12 @@ sub dump_struct($$) {
$clause =~ s/\s+$//; $clause =~ s/\s+$//;
$clause =~ s/\s+/ /; $clause =~ s/\s+/ /;
next if (!$clause); next if (!$clause);
$level-- if ($clause =~ m/(})/ && $level > 1); $level-- if ($clause =~ m/(\})/ && $level > 1);
if (!($clause =~ m/^\s*#/)) { if (!($clause =~ m/^\s*#/)) {
$declaration .= "\t" x $level; $declaration .= "\t" x $level;
} }
$declaration .= "\t" . $clause . "\n"; $declaration .= "\t" . $clause . "\n";
$level++ if ($clause =~ m/({)/ && !($clause =~m/}/)); $level++ if ($clause =~ m/(\{)/ && !($clause =~m/\}/));
} }
output_declaration($declaration_name, output_declaration($declaration_name,
'struct', 'struct',
@ -1244,7 +1245,7 @@ sub dump_enum($$) {
# strip #define macros inside enums # strip #define macros inside enums
$x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos; $x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos;
if ($x =~ /enum\s+(\w+)\s*{(.*)}/) { if ($x =~ /enum\s+(\w+)\s*\{(.*)\}/) {
$declaration_name = $1; $declaration_name = $1;
my $members = $2; my $members = $2;
my %_members; my %_members;
@ -1381,7 +1382,7 @@ sub create_parameterlist($$$$) {
} elsif ($arg =~ m/\(.+\)\s*\(/) { } elsif ($arg =~ m/\(.+\)\s*\(/) {
# pointer-to-function # pointer-to-function
$arg =~ tr/#/,/; $arg =~ tr/#/,/;
$arg =~ m/[^\(]+\(\*?\s*([\w\.]*)\s*\)/; $arg =~ m/[^\(]+\([\w\s]*\*?\s*([\w\.]*)\s*\)/;
$param = $1; $param = $1;
$type = $arg; $type = $arg;
$type =~ s/([^\(]+\(\*?)\s*$param/$1/; $type =~ s/([^\(]+\(\*?)\s*$param/$1/;
@ -1473,7 +1474,7 @@ sub push_parameter($$$$) {
if (!defined $parameterdescs{$param} && $param !~ /^#/) { if (!defined $parameterdescs{$param} && $param !~ /^#/) {
$parameterdescs{$param} = $undescribed; $parameterdescs{$param} = $undescribed;
if (show_warnings($type, $declaration_name)) { if (show_warnings($type, $declaration_name) && $param !~ /\./) {
print STDERR print STDERR
"${file}:$.: warning: Function parameter or member '$param' not described in '$declaration_name'\n"; "${file}:$.: warning: Function parameter or member '$param' not described in '$declaration_name'\n";
++$warnings; ++$warnings;
@ -1785,7 +1786,7 @@ sub process_proto_type($$) {
} }
while (1) { while (1) {
if ( $x =~ /([^{};]*)([{};])(.*)/ ) { if ( $x =~ /([^\{\};]*)([\{\};])(.*)/ ) {
if( length $prototype ) { if( length $prototype ) {
$prototype .= " " $prototype .= " "
} }
@ -1904,13 +1905,13 @@ sub process_name($$) {
++$warnings; ++$warnings;
} }
if ($identifier =~ m/^struct/) { if ($identifier =~ m/^struct\b/) {
$decl_type = 'struct'; $decl_type = 'struct';
} elsif ($identifier =~ m/^union/) { } elsif ($identifier =~ m/^union\b/) {
$decl_type = 'union'; $decl_type = 'union';
} elsif ($identifier =~ m/^enum/) { } elsif ($identifier =~ m/^enum\b/) {
$decl_type = 'enum'; $decl_type = 'enum';
} elsif ($identifier =~ m/^typedef/) { } elsif ($identifier =~ m/^typedef\b/) {
$decl_type = 'typedef'; $decl_type = 'typedef';
} else { } else {
$decl_type = 'function'; $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: This tests Android Verified Boot 2.0 support in U-boot:
For additional details about how to build proper vbmeta partition For additional details about how to build proper vbmeta partition
check doc/README.avb2 check doc/android/avb2.txt
For configuration verification: For configuration verification:
- Corrupt boot partition and check for failure - Corrupt boot partition and check for failure