efi_loader: refactor boot device and loaded_image handling
Get rid of the hacky fake boot-device and duplicate device-path constructing (which needs to match what efi_disk and efi_net do). Instead convert over to use efi_device_path helpers to construct device-paths, and use that to look up the actual boot device. Also, extract out a helper to plug things in properly to the loaded_image. In a following patch we'll want to re-use this in efi_load_image() to handle the case of loading an image from a file_path. Signed-off-by: Rob Clark <robdclark@gmail.com> Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
		
							parent
							
								
									e15fc33548
								
							
						
					
					
						commit
						95c5553ea2
					
				
							
								
								
									
										199
									
								
								cmd/bootefi.c
								
								
								
								
							
							
						
						
									
										199
									
								
								cmd/bootefi.c
								
								
								
								
							|  | @ -22,97 +22,14 @@ DECLARE_GLOBAL_DATA_PTR; | ||||||
| 
 | 
 | ||||||
| static uint8_t efi_obj_list_initalized; | static uint8_t efi_obj_list_initalized; | ||||||
| 
 | 
 | ||||||
| /*
 | static struct efi_device_path *bootefi_image_path; | ||||||
|  * When booting using the "bootefi" command, we don't know which | static struct efi_device_path *bootefi_device_path; | ||||||
|  * physical device the file came from. So we create a pseudo-device |  | ||||||
|  * called "bootefi" with the device path /bootefi. |  | ||||||
|  * |  | ||||||
|  * In addition to the originating device we also declare the file path |  | ||||||
|  * of "bootefi" based loads to be /bootefi. |  | ||||||
|  */ |  | ||||||
| static struct efi_device_path_file_path bootefi_image_path[] = { |  | ||||||
| 	{ |  | ||||||
| 		.dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE, |  | ||||||
| 		.dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH, |  | ||||||
| 		.dp.length = sizeof(bootefi_image_path[0]), |  | ||||||
| 		.str = { 'b','o','o','t','e','f','i' }, |  | ||||||
| 	}, { |  | ||||||
| 		.dp.type = DEVICE_PATH_TYPE_END, |  | ||||||
| 		.dp.sub_type = DEVICE_PATH_SUB_TYPE_END, |  | ||||||
| 		.dp.length = sizeof(bootefi_image_path[0]), |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static struct efi_device_path_file_path bootefi_device_path[] = { |  | ||||||
| 	{ |  | ||||||
| 		.dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE, |  | ||||||
| 		.dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH, |  | ||||||
| 		.dp.length = sizeof(bootefi_image_path[0]), |  | ||||||
| 		.str = { 'b','o','o','t','e','f','i' }, |  | ||||||
| 	}, { |  | ||||||
| 		.dp.type = DEVICE_PATH_TYPE_END, |  | ||||||
| 		.dp.sub_type = DEVICE_PATH_SUB_TYPE_END, |  | ||||||
| 		.dp.length = sizeof(bootefi_image_path[0]), |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /* The EFI loaded_image interface for the image executed via "bootefi" */ |  | ||||||
| static struct efi_loaded_image loaded_image_info = { |  | ||||||
| 	.device_handle = bootefi_device_path, |  | ||||||
| 	.file_path = bootefi_image_path, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /* The EFI object struct for the image executed via "bootefi" */ |  | ||||||
| static struct efi_object loaded_image_info_obj = { |  | ||||||
| 	.handle = &loaded_image_info, |  | ||||||
| 	.protocols = { |  | ||||||
| 		{ |  | ||||||
| 			/*
 |  | ||||||
| 			 * When asking for the loaded_image interface, just |  | ||||||
| 			 * return handle which points to loaded_image_info |  | ||||||
| 			 */ |  | ||||||
| 			.guid = &efi_guid_loaded_image, |  | ||||||
| 			.protocol_interface = &loaded_image_info, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			/*
 |  | ||||||
| 			 * When asking for the device path interface, return |  | ||||||
| 			 * bootefi_device_path |  | ||||||
| 			 */ |  | ||||||
| 			.guid = &efi_guid_device_path, |  | ||||||
| 			.protocol_interface = bootefi_device_path, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			.guid = &efi_guid_console_control, |  | ||||||
| 			.protocol_interface = (void *) &efi_console_control |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			.guid = &efi_guid_device_path_to_text_protocol, |  | ||||||
| 			.protocol_interface = (void *) &efi_device_path_to_text |  | ||||||
| 		}, |  | ||||||
| 	}, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /* The EFI object struct for the device the "bootefi" image was loaded from */ |  | ||||||
| static struct efi_object bootefi_device_obj = { |  | ||||||
| 	.handle = bootefi_device_path, |  | ||||||
| 	.protocols = { |  | ||||||
| 		{ |  | ||||||
| 			/* When asking for the device path interface, return
 |  | ||||||
| 			 * bootefi_device_path */ |  | ||||||
| 			.guid = &efi_guid_device_path, |  | ||||||
| 			.protocol_interface = bootefi_device_path |  | ||||||
| 		} |  | ||||||
| 	}, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| /* Initialize and populate EFI object list */ | /* Initialize and populate EFI object list */ | ||||||
| static void efi_init_obj_list(void) | static void efi_init_obj_list(void) | ||||||
| { | { | ||||||
| 	efi_obj_list_initalized = 1; | 	efi_obj_list_initalized = 1; | ||||||
| 
 | 
 | ||||||
| 	list_add_tail(&loaded_image_info_obj.link, &efi_obj_list); |  | ||||||
| 	list_add_tail(&bootefi_device_obj.link, &efi_obj_list); |  | ||||||
| 	efi_console_register(); | 	efi_console_register(); | ||||||
| #ifdef CONFIG_PARTITIONS | #ifdef CONFIG_PARTITIONS | ||||||
| 	efi_disk_register(); | 	efi_disk_register(); | ||||||
|  | @ -121,13 +38,7 @@ static void efi_init_obj_list(void) | ||||||
| 	efi_gop_register(); | 	efi_gop_register(); | ||||||
| #endif | #endif | ||||||
| #ifdef CONFIG_NET | #ifdef CONFIG_NET | ||||||
| 	void *nethandle = loaded_image_info.device_handle; | 	efi_net_register(); | ||||||
| 	efi_net_register(&nethandle); |  | ||||||
| 
 |  | ||||||
| 	if (!memcmp(bootefi_device_path[0].str, "N\0e\0t", 6)) |  | ||||||
| 		loaded_image_info.device_handle = nethandle; |  | ||||||
| 	else |  | ||||||
| 		loaded_image_info.device_handle = bootefi_device_path; |  | ||||||
| #endif | #endif | ||||||
| #ifdef CONFIG_GENERATE_SMBIOS_TABLE | #ifdef CONFIG_GENERATE_SMBIOS_TABLE | ||||||
| 	efi_smbios_register(); | 	efi_smbios_register(); | ||||||
|  | @ -210,14 +121,27 @@ static unsigned long efi_run_in_el2(asmlinkage ulong (*entry)( | ||||||
|  * Load an EFI payload into a newly allocated piece of memory, register all |  * Load an EFI payload into a newly allocated piece of memory, register all | ||||||
|  * EFI objects it would want to access and jump to it. |  * EFI objects it would want to access and jump to it. | ||||||
|  */ |  */ | ||||||
| static unsigned long do_bootefi_exec(void *efi, void *fdt) | static unsigned long do_bootefi_exec(void *efi, void *fdt, | ||||||
|  | 				     struct efi_device_path *device_path, | ||||||
|  | 				     struct efi_device_path *image_path) | ||||||
| { | { | ||||||
|  | 	struct efi_loaded_image loaded_image_info = {}; | ||||||
|  | 	struct efi_object loaded_image_info_obj = {}; | ||||||
|  | 	ulong ret; | ||||||
|  | 
 | ||||||
| 	ulong (*entry)(void *image_handle, struct efi_system_table *st) | 	ulong (*entry)(void *image_handle, struct efi_system_table *st) | ||||||
| 		asmlinkage; | 		asmlinkage; | ||||||
| 	ulong fdt_pages, fdt_size, fdt_start, fdt_end; | 	ulong fdt_pages, fdt_size, fdt_start, fdt_end; | ||||||
| 	const efi_guid_t fdt_guid = EFI_FDT_GUID; | 	const efi_guid_t fdt_guid = EFI_FDT_GUID; | ||||||
| 	bootm_headers_t img = { 0 }; | 	bootm_headers_t img = { 0 }; | ||||||
| 
 | 
 | ||||||
|  | 	/* Initialize and populate EFI object list */ | ||||||
|  | 	if (!efi_obj_list_initalized) | ||||||
|  | 		efi_init_obj_list(); | ||||||
|  | 
 | ||||||
|  | 	efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj, | ||||||
|  | 			       device_path, image_path); | ||||||
|  | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * gd lives in a fixed register which may get clobbered while we execute | 	 * gd lives in a fixed register which may get clobbered while we execute | ||||||
| 	 * the payload. So save it here and restore it on every callback entry | 	 * the payload. So save it here and restore it on every callback entry | ||||||
|  | @ -252,18 +176,18 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt) | ||||||
| 
 | 
 | ||||||
| 	/* Load the EFI payload */ | 	/* Load the EFI payload */ | ||||||
| 	entry = efi_load_pe(efi, &loaded_image_info); | 	entry = efi_load_pe(efi, &loaded_image_info); | ||||||
| 	if (!entry) | 	if (!entry) { | ||||||
| 		return -ENOENT; | 		ret = -ENOENT; | ||||||
| 
 | 		goto exit; | ||||||
| 	/* Initialize and populate EFI object list */ | 	} | ||||||
| 	if (!efi_obj_list_initalized) |  | ||||||
| 		efi_init_obj_list(); |  | ||||||
| 
 | 
 | ||||||
| 	/* Call our payload! */ | 	/* Call our payload! */ | ||||||
| 	debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry); | 	debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry); | ||||||
| 
 | 
 | ||||||
| 	if (setjmp(&loaded_image_info.exit_jmp)) { | 	if (setjmp(&loaded_image_info.exit_jmp)) { | ||||||
| 		return loaded_image_info.exit_status; | 		ret = loaded_image_info.exit_status; | ||||||
|  | 		EFI_EXIT(ret); | ||||||
|  | 		goto exit; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_ARM64 | #ifdef CONFIG_ARM64 | ||||||
|  | @ -282,7 +206,13 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt) | ||||||
| 	} | 	} | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| 	return efi_do_enter(&loaded_image_info, &systab, entry); | 	ret = efi_do_enter(&loaded_image_info, &systab, entry); | ||||||
|  | 
 | ||||||
|  | exit: | ||||||
|  | 	/* image has returned, loaded-image obj goes *poof*: */ | ||||||
|  | 	list_del(&loaded_image_info_obj.link); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Interpreter command to boot an arbitrary EFI image from memory */ | /* Interpreter command to boot an arbitrary EFI image from memory */ | ||||||
|  | @ -334,7 +264,8 @@ static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	printf("## Starting EFI application at %08lx ...\n", addr); | 	printf("## Starting EFI application at %08lx ...\n", addr); | ||||||
| 	r = do_bootefi_exec((void *)addr, (void*)fdt_addr); | 	r = do_bootefi_exec((void *)addr, (void *)fdt_addr, | ||||||
|  | 			    bootefi_device_path, bootefi_image_path); | ||||||
| 	printf("## Application terminated, r = %lu\n", | 	printf("## Application terminated, r = %lu\n", | ||||||
| 	       r & ~EFI_ERROR_MASK); | 	       r & ~EFI_ERROR_MASK); | ||||||
| 
 | 
 | ||||||
|  | @ -367,58 +298,44 @@ U_BOOT_CMD( | ||||||
| 	bootefi_help_text | 	bootefi_help_text | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
|  | static int parse_partnum(const char *devnr) | ||||||
|  | { | ||||||
|  | 	const char *str = strchr(devnr, ':'); | ||||||
|  | 	if (str) { | ||||||
|  | 		str++; | ||||||
|  | 		return simple_strtoul(str, NULL, 16); | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void efi_set_bootdev(const char *dev, const char *devnr, const char *path) | void efi_set_bootdev(const char *dev, const char *devnr, const char *path) | ||||||
| { | { | ||||||
| 	__maybe_unused struct blk_desc *desc; | 	char filename[32] = { 0 }; /* dp->str is u16[32] long */ | ||||||
| 	char devname[32] = { 0 }; /* dp->str is u16[32] long */ | 	char *s; | ||||||
| 	char *colon, *s; | 
 | ||||||
|  | 	if (strcmp(dev, "Net")) { | ||||||
|  | 		struct blk_desc *desc; | ||||||
|  | 		int part; | ||||||
| 
 | 
 | ||||||
| #if defined(CONFIG_BLK) || CONFIG_IS_ENABLED(ISO_PARTITION) |  | ||||||
| 		desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10)); | 		desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10)); | ||||||
| #endif | 		part = parse_partnum(devnr); | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_BLK | 		bootefi_device_path = efi_dp_from_part(desc, part); | ||||||
| 	if (desc) { | 	} else { | ||||||
| 		snprintf(devname, sizeof(devname), "%s", desc->bdev->name); | #ifdef CONFIG_NET | ||||||
| 	} else | 		bootefi_device_path = efi_dp_from_eth(); | ||||||
| #endif | #endif | ||||||
| 
 |  | ||||||
| 	{ |  | ||||||
| 		/* Assemble the condensed device name we use in efi_disk.c */ |  | ||||||
| 		snprintf(devname, sizeof(devname), "%s%s", dev, devnr); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	colon = strchr(devname, ':'); |  | ||||||
| 
 |  | ||||||
| #if CONFIG_IS_ENABLED(ISO_PARTITION) |  | ||||||
| 	/* For ISOs we create partition block devices */ |  | ||||||
| 	if (desc && (desc->type != DEV_TYPE_UNKNOWN) && |  | ||||||
| 	    (desc->part_type == PART_TYPE_ISO)) { |  | ||||||
| 		if (!colon) |  | ||||||
| 			snprintf(devname, sizeof(devname), "%s:1", devname); |  | ||||||
| 
 |  | ||||||
| 		colon = NULL; |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| 	if (colon) |  | ||||||
| 		*colon = '\0'; |  | ||||||
| 
 |  | ||||||
| 	/* Patch bootefi_device_path to the target device */ |  | ||||||
| 	memset(bootefi_device_path[0].str, 0, sizeof(bootefi_device_path[0].str)); |  | ||||||
| 	ascii2unicode(bootefi_device_path[0].str, devname); |  | ||||||
| 
 |  | ||||||
| 	/* Patch bootefi_image_path to the target file path */ |  | ||||||
| 	memset(bootefi_image_path[0].str, 0, sizeof(bootefi_image_path[0].str)); |  | ||||||
| 	if (strcmp(dev, "Net")) { | 	if (strcmp(dev, "Net")) { | ||||||
| 		/* Add leading / to fs paths, because they're absolute */ | 		/* Add leading / to fs paths, because they're absolute */ | ||||||
| 		snprintf(devname, sizeof(devname), "/%s", path); | 		snprintf(filename, sizeof(filename), "/%s", path); | ||||||
| 	} else { | 	} else { | ||||||
| 		snprintf(devname, sizeof(devname), "%s", path); | 		snprintf(filename, sizeof(filename), "%s", path); | ||||||
| 	} | 	} | ||||||
| 	/* DOS style file path: */ | 	/* DOS style file path: */ | ||||||
| 	s = devname; | 	s = filename; | ||||||
| 	while ((s = strchr(s, '/'))) | 	while ((s = strchr(s, '/'))) | ||||||
| 		*s++ = '\\'; | 		*s++ = '\\'; | ||||||
| 	ascii2unicode(bootefi_image_path[0].str, devname); | 	bootefi_image_path = efi_dp_from_file(NULL, 0, filename); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -159,7 +159,7 @@ int efi_disk_register(void); | ||||||
| /* Called by bootefi to make GOP (graphical) interface available */ | /* Called by bootefi to make GOP (graphical) interface available */ | ||||||
| int efi_gop_register(void); | int efi_gop_register(void); | ||||||
| /* Called by bootefi to make the network interface available */ | /* Called by bootefi to make the network interface available */ | ||||||
| int efi_net_register(void **handle); | int efi_net_register(void); | ||||||
| /* Called by bootefi to make SMBIOS tables available */ | /* Called by bootefi to make SMBIOS tables available */ | ||||||
| void efi_smbios_register(void); | void efi_smbios_register(void); | ||||||
| 
 | 
 | ||||||
|  | @ -216,6 +216,9 @@ uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type, | ||||||
| int efi_memory_init(void); | int efi_memory_init(void); | ||||||
| /* Adds new or overrides configuration table entry to the system table */ | /* Adds new or overrides configuration table entry to the system table */ | ||||||
| efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table); | efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table); | ||||||
|  | void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *obj, | ||||||
|  | 			    struct efi_device_path *device_path, | ||||||
|  | 			    struct efi_device_path *file_path); | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER | #ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER | ||||||
| extern void *efi_bounce_buffer; | extern void *efi_bounce_buffer; | ||||||
|  |  | ||||||
|  | @ -777,6 +777,42 @@ static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid, | ||||||
| 	return EFI_EXIT(efi_install_configuration_table(guid, table)); | 	return EFI_EXIT(efi_install_configuration_table(guid, table)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Initialize a loaded_image_info + loaded_image_info object with correct
 | ||||||
|  |  * protocols, boot-device, etc. | ||||||
|  |  */ | ||||||
|  | void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *obj, | ||||||
|  | 			    struct efi_device_path *device_path, | ||||||
|  | 			    struct efi_device_path *file_path) | ||||||
|  | { | ||||||
|  | 	obj->handle = info; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * When asking for the device path interface, return | ||||||
|  | 	 * bootefi_device_path | ||||||
|  | 	 */ | ||||||
|  | 	obj->protocols[0].guid = &efi_guid_device_path; | ||||||
|  | 	obj->protocols[0].protocol_interface = device_path; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * When asking for the loaded_image interface, just | ||||||
|  | 	 * return handle which points to loaded_image_info | ||||||
|  | 	 */ | ||||||
|  | 	obj->protocols[1].guid = &efi_guid_loaded_image; | ||||||
|  | 	obj->protocols[1].protocol_interface = info; | ||||||
|  | 
 | ||||||
|  | 	obj->protocols[2].guid = &efi_guid_console_control; | ||||||
|  | 	obj->protocols[2].protocol_interface = (void *)&efi_console_control; | ||||||
|  | 
 | ||||||
|  | 	obj->protocols[3].guid = &efi_guid_device_path_to_text_protocol; | ||||||
|  | 	obj->protocols[3].protocol_interface = | ||||||
|  | 		(void *)&efi_device_path_to_text; | ||||||
|  | 
 | ||||||
|  | 	info->file_path = file_path; | ||||||
|  | 	info->device_handle = efi_dp_find_obj(device_path, NULL); | ||||||
|  | 
 | ||||||
|  | 	list_add_tail(&obj->link, &efi_obj_list); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static efi_status_t EFIAPI efi_load_image(bool boot_policy, | static efi_status_t EFIAPI efi_load_image(bool boot_policy, | ||||||
| 					  efi_handle_t parent_image, | 					  efi_handle_t parent_image, | ||||||
| 					  struct efi_device_path *file_path, | 					  struct efi_device_path *file_path, | ||||||
|  |  | ||||||
|  | @ -207,7 +207,7 @@ void efi_net_set_dhcp_ack(void *pkt, int len) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* This gets called from do_bootefi_exec(). */ | /* This gets called from do_bootefi_exec(). */ | ||||||
| int efi_net_register(void **handle) | int efi_net_register(void) | ||||||
| { | { | ||||||
| 	struct efi_net_obj *netobj; | 	struct efi_net_obj *netobj; | ||||||
| 
 | 
 | ||||||
|  | @ -253,8 +253,5 @@ int efi_net_register(void **handle) | ||||||
| 	/* Hook net up to the device list */ | 	/* Hook net up to the device list */ | ||||||
| 	list_add_tail(&netobj->parent.link, &efi_obj_list); | 	list_add_tail(&netobj->parent.link, &efi_obj_list); | ||||||
| 
 | 
 | ||||||
| 	if (handle) |  | ||||||
| 		*handle = &netobj->net; |  | ||||||
| 
 |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue