mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-27 08:33:10 +01:00 
			
		
		
		
	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:
		
							
								
								
									
										1
									
								
								Documentation/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								Documentation/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1 @@ | ||||
| output | ||||
| *.pyc | ||||
|   | ||||
| @@ -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(); | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|   | ||||
							
								
								
									
										17
									
								
								cmd/Kconfig
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								cmd/Kconfig
									
									
									
									
									
								
							| @@ -735,7 +735,7 @@ config CMD_FASTBOOT | ||||
| 	  Android devices. Fastboot requires either the network stack | ||||
| 	  enabled or support for acting as a USB device. | ||||
|  | ||||
| 	  See doc/README.android-fastboot for more information. | ||||
| 	  See doc/android/fastboot.txt for more information. | ||||
|  | ||||
| config CMD_FDC | ||||
| 	bool "fdcboot - Boot from floppy device" | ||||
| @@ -1198,6 +1198,21 @@ config CMD_SETEXPR | ||||
|  | ||||
| endmenu | ||||
|  | ||||
| menu "Android support commands" | ||||
|  | ||||
| config CMD_AB_SELECT | ||||
| 	bool "ab_select" | ||||
| 	default n | ||||
| 	depends on ANDROID_AB | ||||
| 	help | ||||
| 	  On Android devices with more than one boot slot (multiple copies of | ||||
| 	  the kernel and system images) this provides a command to select which | ||||
| 	  slot should be used to boot from and register the boot attempt. This | ||||
| 	  is used by the new A/B update model where one slot is updated in the | ||||
| 	  background while running from the other slot. | ||||
|  | ||||
| endmenu | ||||
|  | ||||
| if NET | ||||
|  | ||||
| menuconfig CMD_NET | ||||
|   | ||||
| @@ -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
									
								
							
							
						
						
									
										52
									
								
								cmd/ab_select.c
									
									
									
									
									
										Normal 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" | ||||
| ); | ||||
							
								
								
									
										68
									
								
								cmd/bcb.c
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								cmd/bcb.c
									
									
									
									
									
								
							| @@ -24,17 +24,17 @@ static struct bootloader_message bcb = { { 0 } }; | ||||
|  | ||||
| static int bcb_cmd_get(char *cmd) | ||||
| { | ||||
| 	if (!strncmp(cmd, "load", sizeof("load"))) | ||||
| 	if (!strcmp(cmd, "load")) | ||||
| 		return BCB_CMD_LOAD; | ||||
| 	if (!strncmp(cmd, "set", sizeof("set"))) | ||||
| 	if (!strcmp(cmd, "set")) | ||||
| 		return BCB_CMD_FIELD_SET; | ||||
| 	if (!strncmp(cmd, "clear", sizeof("clear"))) | ||||
| 	if (!strcmp(cmd, "clear")) | ||||
| 		return BCB_CMD_FIELD_CLEAR; | ||||
| 	if (!strncmp(cmd, "test", sizeof("test"))) | ||||
| 	if (!strcmp(cmd, "test")) | ||||
| 		return BCB_CMD_FIELD_TEST; | ||||
| 	if (!strncmp(cmd, "store", sizeof("store"))) | ||||
| 	if (!strcmp(cmd, "store")) | ||||
| 		return BCB_CMD_STORE; | ||||
| 	if (!strncmp(cmd, "dump", sizeof("dump"))) | ||||
| 	if (!strcmp(cmd, "dump")) | ||||
| 		return BCB_CMD_FIELD_DUMP; | ||||
| 	else | ||||
| 		return -1; | ||||
| @@ -46,9 +46,6 @@ static int bcb_is_misused(int argc, char *const argv[]) | ||||
|  | ||||
| 	switch (cmd) { | ||||
| 	case BCB_CMD_LOAD: | ||||
| 		if (argc != 3) | ||||
| 			goto err; | ||||
| 		break; | ||||
| 	case BCB_CMD_FIELD_SET: | ||||
| 		if (argc != 3) | ||||
| 			goto err; | ||||
| @@ -86,23 +83,23 @@ err: | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| static int bcb_field_get(char *name, char **field, int *size) | ||||
| static int bcb_field_get(char *name, char **fieldp, int *sizep) | ||||
| { | ||||
| 	if (!strncmp(name, "command", sizeof("command"))) { | ||||
| 		*field = bcb.command; | ||||
| 		*size = sizeof(bcb.command); | ||||
| 	} else if (!strncmp(name, "status", sizeof("status"))) { | ||||
| 		*field = bcb.status; | ||||
| 		*size = sizeof(bcb.status); | ||||
| 	} else if (!strncmp(name, "recovery", sizeof("recovery"))) { | ||||
| 		*field = bcb.recovery; | ||||
| 		*size = sizeof(bcb.recovery); | ||||
| 	} else if (!strncmp(name, "stage", sizeof("stage"))) { | ||||
| 		*field = bcb.stage; | ||||
| 		*size = sizeof(bcb.stage); | ||||
| 	} else if (!strncmp(name, "reserved", sizeof("reserved"))) { | ||||
| 		*field = bcb.reserved; | ||||
| 		*size = sizeof(bcb.reserved); | ||||
| 	if (!strcmp(name, "command")) { | ||||
| 		*fieldp = bcb.command; | ||||
| 		*sizep = sizeof(bcb.command); | ||||
| 	} else if (!strcmp(name, "status")) { | ||||
| 		*fieldp = bcb.status; | ||||
| 		*sizep = sizeof(bcb.status); | ||||
| 	} else if (!strcmp(name, "recovery")) { | ||||
| 		*fieldp = bcb.recovery; | ||||
| 		*sizep = sizeof(bcb.recovery); | ||||
| 	} else if (!strcmp(name, "stage")) { | ||||
| 		*fieldp = bcb.stage; | ||||
| 		*sizep = sizeof(bcb.stage); | ||||
| 	} else if (!strcmp(name, "reserved")) { | ||||
| 		*fieldp = bcb.reserved; | ||||
| 		*sizep = sizeof(bcb.reserved); | ||||
| 	} else { | ||||
| 		printf("Error: Unknown bcb field '%s'\n", name); | ||||
| 		return -1; | ||||
| @@ -111,8 +108,8 @@ static int bcb_field_get(char *name, char **field, int *size) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| do_bcb_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | ||||
| static int do_bcb_load(cmd_tbl_t *cmdtp, int flag, int argc, | ||||
| 		       char * const argv[]) | ||||
| { | ||||
| 	struct blk_desc *desc; | ||||
| 	disk_partition_t info; | ||||
| @@ -122,28 +119,28 @@ do_bcb_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | ||||
|  | ||||
| 	ret = blk_get_device_by_str("mmc", argv[1], &desc); | ||||
| 	if (ret < 0) | ||||
| 		goto err_1; | ||||
| 		goto err_read_fail; | ||||
|  | ||||
| 	part = simple_strtoul(argv[2], &endp, 0); | ||||
| 	if (*endp == '\0') { | ||||
| 		ret = part_get_info(desc, part, &info); | ||||
| 		if (ret) | ||||
| 			goto err_1; | ||||
| 			goto err_read_fail; | ||||
| 	} else { | ||||
| 		part = part_get_info_by_name(desc, argv[2], &info); | ||||
| 		if (part < 0) { | ||||
| 			ret = part; | ||||
| 			goto err_1; | ||||
| 			goto err_read_fail; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	cnt = DIV_ROUND_UP(sizeof(struct bootloader_message), info.blksz); | ||||
| 	if (cnt > info.size) | ||||
| 		goto err_2; | ||||
| 		goto err_too_small; | ||||
|  | ||||
| 	if (blk_dread(desc, info.start, cnt, &bcb) != cnt) { | ||||
| 		ret = -EIO; | ||||
| 		goto err_1; | ||||
| 		goto err_read_fail; | ||||
| 	} | ||||
|  | ||||
| 	bcb_dev = desc->devnum; | ||||
| @@ -151,10 +148,10 @@ do_bcb_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | ||||
| 	debug("%s: Loaded from mmc %d:%d\n", __func__, bcb_dev, bcb_part); | ||||
|  | ||||
| 	return CMD_RET_SUCCESS; | ||||
| err_1: | ||||
| err_read_fail: | ||||
| 	printf("Error: mmc %s:%s read failed (%d)\n", argv[1], argv[2], ret); | ||||
| 	goto err; | ||||
| err_2: | ||||
| err_too_small: | ||||
| 	printf("Error: mmc %s:%s too small!", argv[1], argv[2]); | ||||
| 	goto err; | ||||
| err: | ||||
| @@ -307,7 +304,8 @@ static int do_bcb(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) | ||||
| 		return CMD_RET_USAGE; | ||||
|  | ||||
| 	if (bcb_is_misused(argc, argv)) { | ||||
| 		/* We try to improve the user experience by reporting the | ||||
| 		/* | ||||
| 		 * We try to improve the user experience by reporting the | ||||
| 		 * root-cause of misusage, so don't return CMD_RET_USAGE, | ||||
| 		 * since the latter prints out the full-blown help text | ||||
| 		 */ | ||||
|   | ||||
							
								
								
									
										16
									
								
								cmd/part.c
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								cmd/part.c
									
									
									
									
									
								
							| @@ -24,6 +24,7 @@ | ||||
| enum cmd_part_info { | ||||
| 	CMD_PART_INFO_START = 0, | ||||
| 	CMD_PART_INFO_SIZE, | ||||
| 	CMD_PART_INFO_NUMBER | ||||
| }; | ||||
|  | ||||
| static int do_part_uuid(int argc, char * const argv[]) | ||||
| @@ -149,6 +150,9 @@ static int do_part_info(int argc, char * const argv[], enum cmd_part_info param) | ||||
| 	case CMD_PART_INFO_SIZE: | ||||
| 		snprintf(buf, sizeof(buf), LBAF, info.size); | ||||
| 		break; | ||||
| 	case CMD_PART_INFO_NUMBER: | ||||
| 		snprintf(buf, sizeof(buf), "%d", part); | ||||
| 		break; | ||||
| 	default: | ||||
| 		printf("** Unknown cmd_part_info value: %d\n", param); | ||||
| 		return 1; | ||||
| @@ -172,6 +176,11 @@ static int do_part_size(int argc, char * const argv[]) | ||||
| 	return do_part_info(argc, argv, CMD_PART_INFO_SIZE); | ||||
| } | ||||
|  | ||||
| static int do_part_number(int argc, char * const argv[]) | ||||
| { | ||||
| 	return do_part_info(argc, argv, CMD_PART_INFO_NUMBER); | ||||
| } | ||||
|  | ||||
| static int do_part(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | ||||
| { | ||||
| 	if (argc < 2) | ||||
| @@ -185,6 +194,8 @@ static int do_part(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | ||||
| 		return do_part_start(argc - 2, argv + 2); | ||||
| 	else if (!strcmp(argv[1], "size")) | ||||
| 		return do_part_size(argc - 2, argv + 2); | ||||
| 	else if (!strcmp(argv[1], "number")) | ||||
| 		return do_part_number(argc - 2, argv + 2); | ||||
|  | ||||
| 	return CMD_RET_USAGE; | ||||
| } | ||||
| @@ -206,5 +217,8 @@ U_BOOT_CMD( | ||||
| 	"      part can be either partition number or partition name\n" | ||||
| 	"part size <interface> <dev> <part> <varname>\n" | ||||
| 	"    - set environment variable to the size of the partition (in blocks)\n" | ||||
| 	"      part can be either partition number or partition name" | ||||
| 	"      part can be either partition number or partition name\n" | ||||
| 	"part number <interface> <dev> <part> <varname>\n" | ||||
| 	"    - set environment variable to the partition number using the partition name\n" | ||||
| 	"      part must be specified as partition name" | ||||
| ); | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -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
									
								
							
							
						
						
									
										300
									
								
								common/android_ab.c
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										68
									
								
								disk/part.c
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								disk/part.c
									
									
									
									
									
								
							| @@ -674,6 +674,74 @@ int part_get_info_by_name(struct blk_desc *dev_desc, const char *name, | ||||
| 	return part_get_info_by_name_type(dev_desc, name, info, PART_TYPE_ALL); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Get partition info from device number and partition name. | ||||
|  * | ||||
|  * Parse a device number and partition name string in the form of | ||||
|  * "device_num#partition_name", for example "0#misc". If the partition | ||||
|  * is found, sets dev_desc and part_info accordingly with the information | ||||
|  * of the partition with the given partition_name. | ||||
|  * | ||||
|  * @param[in] dev_iface Device interface | ||||
|  * @param[in] dev_part_str Input string argument, like "0#misc" | ||||
|  * @param[out] dev_desc Place to store the device description pointer | ||||
|  * @param[out] part_info Place to store the partition information | ||||
|  * @return 0 on success, or a negative on error | ||||
|  */ | ||||
| static int part_get_info_by_dev_and_name(const char *dev_iface, | ||||
| 					 const char *dev_part_str, | ||||
| 					 struct blk_desc **dev_desc, | ||||
| 					 disk_partition_t *part_info) | ||||
| { | ||||
| 	char *ep; | ||||
| 	const char *part_str; | ||||
| 	int dev_num; | ||||
|  | ||||
| 	part_str = strchr(dev_part_str, '#'); | ||||
| 	if (!part_str || part_str == dev_part_str) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	dev_num = simple_strtoul(dev_part_str, &ep, 16); | ||||
| 	if (ep != part_str) { | ||||
| 		/* Not all the first part before the # was parsed. */ | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	part_str++; | ||||
|  | ||||
| 	*dev_desc = blk_get_dev(dev_iface, dev_num); | ||||
| 	if (!*dev_desc) { | ||||
| 		printf("Could not find %s %d\n", dev_iface, dev_num); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	if (part_get_info_by_name(*dev_desc, part_str, part_info) < 0) { | ||||
| 		printf("Could not find \"%s\" partition\n", part_str); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int part_get_info_by_dev_and_name_or_num(const char *dev_iface, | ||||
| 					 const char *dev_part_str, | ||||
| 					 struct blk_desc **dev_desc, | ||||
| 					 disk_partition_t *part_info) | ||||
| { | ||||
| 	/* Split the part_name if passed as "$dev_num#part_name". */ | ||||
| 	if (!part_get_info_by_dev_and_name(dev_iface, dev_part_str, | ||||
| 					   dev_desc, part_info)) | ||||
| 		return 0; | ||||
| 	/* | ||||
| 	 * Couldn't lookup by name, try looking up the partition description | ||||
| 	 * directly. | ||||
| 	 */ | ||||
| 	if (blk_get_device_part_str(dev_iface, dev_part_str, | ||||
| 				    dev_desc, part_info, 1) < 0) { | ||||
| 		printf("Couldn't find partition %s %s\n", | ||||
| 		       dev_iface, dev_part_str); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void part_set_generic_name(const struct blk_desc *dev_desc, | ||||
| 	int part_num, char *name) | ||||
| { | ||||
|   | ||||
							
								
								
									
										1
									
								
								doc/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								doc/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| output | ||||
							
								
								
									
										67
									
								
								doc/android/ab.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								doc/android/ab.txt
									
									
									
									
									
										Normal 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 | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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
									
								
							
							
						
						
									
										28
									
								
								doc/sphinx/kernellog.py
									
									
									
									
									
										Normal 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) | ||||
|  | ||||
|  | ||||
| @@ -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] | ||||
|   | ||||
| @@ -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
									
								
							
							
						
						
									
										34
									
								
								include/android_ab.h
									
									
									
									
									
										Normal 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 */ | ||||
| @@ -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" | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|  * | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -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'; | ||||
|   | ||||
							
								
								
									
										75
									
								
								test/py/tests/test_android/test_ab.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								test/py/tests/test_android/test_ab.py
									
									
									
									
									
										Normal 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 | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user