xref: /openbmc/u-boot/cmd/bootefi.c (revision 2074f700)
1b9939336SAlexander Graf /*
2b9939336SAlexander Graf  *  EFI application loader
3b9939336SAlexander Graf  *
4b9939336SAlexander Graf  *  Copyright (c) 2016 Alexander Graf
5b9939336SAlexander Graf  *
6b9939336SAlexander Graf  *  SPDX-License-Identifier:     GPL-2.0+
7b9939336SAlexander Graf  */
8b9939336SAlexander Graf 
9d78e40d6SHeinrich Schuchardt #include <charset.h>
10b9939336SAlexander Graf #include <common.h>
11b9939336SAlexander Graf #include <command.h>
129d922450SSimon Glass #include <dm.h>
13b9939336SAlexander Graf #include <efi_loader.h>
14d78e40d6SHeinrich Schuchardt #include <efi_selftest.h>
15b9939336SAlexander Graf #include <errno.h>
16b9939336SAlexander Graf #include <libfdt.h>
17b9939336SAlexander Graf #include <libfdt_env.h>
18ad0c1a3dSAlexander Graf #include <memalign.h>
190d9d501fSAlexander Graf #include <asm/global_data.h>
20e275458cSSimon Glass #include <asm-generic/sections.h>
21e275458cSSimon Glass #include <linux/linkage.h>
220d9d501fSAlexander Graf 
230d9d501fSAlexander Graf DECLARE_GLOBAL_DATA_PTR;
24b9939336SAlexander Graf 
257cbc1241SHeinrich Schuchardt static uint8_t efi_obj_list_initalized;
267cbc1241SHeinrich Schuchardt 
2795c5553eSRob Clark static struct efi_device_path *bootefi_image_path;
2895c5553eSRob Clark static struct efi_device_path *bootefi_device_path;
29b9939336SAlexander Graf 
307cbc1241SHeinrich Schuchardt /* Initialize and populate EFI object list */
317cbc1241SHeinrich Schuchardt static void efi_init_obj_list(void)
327cbc1241SHeinrich Schuchardt {
337cbc1241SHeinrich Schuchardt 	efi_obj_list_initalized = 1;
347cbc1241SHeinrich Schuchardt 
357cbc1241SHeinrich Schuchardt 	efi_console_register();
367cbc1241SHeinrich Schuchardt #ifdef CONFIG_PARTITIONS
377cbc1241SHeinrich Schuchardt 	efi_disk_register();
387cbc1241SHeinrich Schuchardt #endif
397cbc1241SHeinrich Schuchardt #if defined(CONFIG_LCD) || defined(CONFIG_DM_VIDEO)
407cbc1241SHeinrich Schuchardt 	efi_gop_register();
417cbc1241SHeinrich Schuchardt #endif
427cbc1241SHeinrich Schuchardt #ifdef CONFIG_NET
4395c5553eSRob Clark 	efi_net_register();
447cbc1241SHeinrich Schuchardt #endif
457cbc1241SHeinrich Schuchardt #ifdef CONFIG_GENERATE_SMBIOS_TABLE
467cbc1241SHeinrich Schuchardt 	efi_smbios_register();
477cbc1241SHeinrich Schuchardt #endif
48b3d60900SHeinrich Schuchardt 	efi_watchdog_register();
497cbc1241SHeinrich Schuchardt 
507cbc1241SHeinrich Schuchardt 	/* Initialize EFI runtime services */
517cbc1241SHeinrich Schuchardt 	efi_reset_system_init();
527cbc1241SHeinrich Schuchardt 	efi_get_time_init();
537cbc1241SHeinrich Schuchardt }
547cbc1241SHeinrich Schuchardt 
55d78e40d6SHeinrich Schuchardt /*
56d78e40d6SHeinrich Schuchardt  * Set the load options of an image from an environment variable.
57d78e40d6SHeinrich Schuchardt  *
58d78e40d6SHeinrich Schuchardt  * @loaded_image_info:	the image
59d78e40d6SHeinrich Schuchardt  * @env_var:		name of the environment variable
60d78e40d6SHeinrich Schuchardt  */
61d78e40d6SHeinrich Schuchardt static void set_load_options(struct efi_loaded_image *loaded_image_info,
62d78e40d6SHeinrich Schuchardt 			     const char *env_var)
63d78e40d6SHeinrich Schuchardt {
64d78e40d6SHeinrich Schuchardt 	size_t size;
65d78e40d6SHeinrich Schuchardt 	const char *env = env_get(env_var);
66d78e40d6SHeinrich Schuchardt 
67d78e40d6SHeinrich Schuchardt 	loaded_image_info->load_options = NULL;
68d78e40d6SHeinrich Schuchardt 	loaded_image_info->load_options_size = 0;
69d78e40d6SHeinrich Schuchardt 	if (!env)
70d78e40d6SHeinrich Schuchardt 		return;
71d78e40d6SHeinrich Schuchardt 	size = strlen(env) + 1;
72d78e40d6SHeinrich Schuchardt 	loaded_image_info->load_options = calloc(size, sizeof(u16));
73d78e40d6SHeinrich Schuchardt 	if (!loaded_image_info->load_options) {
74d78e40d6SHeinrich Schuchardt 		printf("ERROR: Out of memory\n");
75d78e40d6SHeinrich Schuchardt 		return;
76d78e40d6SHeinrich Schuchardt 	}
77d78e40d6SHeinrich Schuchardt 	utf8_to_utf16(loaded_image_info->load_options, (u8 *)env, size);
78d78e40d6SHeinrich Schuchardt 	loaded_image_info->load_options_size = size * 2;
79d78e40d6SHeinrich Schuchardt }
80d78e40d6SHeinrich Schuchardt 
810d9d501fSAlexander Graf static void *copy_fdt(void *fdt)
820d9d501fSAlexander Graf {
830d9d501fSAlexander Graf 	u64 fdt_size = fdt_totalsize(fdt);
84ad0c1a3dSAlexander Graf 	unsigned long fdt_ram_start = -1L, fdt_pages;
85ad0c1a3dSAlexander Graf 	u64 new_fdt_addr;
860d9d501fSAlexander Graf 	void *new_fdt;
87ad0c1a3dSAlexander Graf 	int i;
880d9d501fSAlexander Graf 
89ad0c1a3dSAlexander Graf         for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
90ad0c1a3dSAlexander Graf                 u64 ram_start = gd->bd->bi_dram[i].start;
91ad0c1a3dSAlexander Graf                 u64 ram_size = gd->bd->bi_dram[i].size;
920d9d501fSAlexander Graf 
93ad0c1a3dSAlexander Graf 		if (!ram_size)
94ad0c1a3dSAlexander Graf 			continue;
95ad0c1a3dSAlexander Graf 
96ad0c1a3dSAlexander Graf 		if (ram_start < fdt_ram_start)
97ad0c1a3dSAlexander Graf 			fdt_ram_start = ram_start;
98ad0c1a3dSAlexander Graf 	}
99ad0c1a3dSAlexander Graf 
100ad0c1a3dSAlexander Graf 	/* Give us at least 4kb breathing room */
101a44bffccSxypron.glpk@gmx.de 	fdt_size = ALIGN(fdt_size + 4096, EFI_PAGE_SIZE);
102ad0c1a3dSAlexander Graf 	fdt_pages = fdt_size >> EFI_PAGE_SHIFT;
103ad0c1a3dSAlexander Graf 
104ad0c1a3dSAlexander Graf 	/* Safe fdt location is at 128MB */
105ad0c1a3dSAlexander Graf 	new_fdt_addr = fdt_ram_start + (128 * 1024 * 1024) + fdt_size;
106ad0c1a3dSAlexander Graf 	if (efi_allocate_pages(1, EFI_BOOT_SERVICES_DATA, fdt_pages,
107ad0c1a3dSAlexander Graf 			       &new_fdt_addr) != EFI_SUCCESS) {
108ad0c1a3dSAlexander Graf 		/* If we can't put it there, put it somewhere */
109a44bffccSxypron.glpk@gmx.de 		new_fdt_addr = (ulong)memalign(EFI_PAGE_SIZE, fdt_size);
11085a6e9b3SAlexander Graf 		if (efi_allocate_pages(1, EFI_BOOT_SERVICES_DATA, fdt_pages,
11185a6e9b3SAlexander Graf 				       &new_fdt_addr) != EFI_SUCCESS) {
11285a6e9b3SAlexander Graf 			printf("ERROR: Failed to reserve space for FDT\n");
11385a6e9b3SAlexander Graf 			return NULL;
114ad0c1a3dSAlexander Graf 		}
11585a6e9b3SAlexander Graf 	}
11685a6e9b3SAlexander Graf 
117ad0c1a3dSAlexander Graf 	new_fdt = (void*)(ulong)new_fdt_addr;
1180d9d501fSAlexander Graf 	memcpy(new_fdt, fdt, fdt_totalsize(fdt));
1190d9d501fSAlexander Graf 	fdt_set_totalsize(new_fdt, fdt_size);
1200d9d501fSAlexander Graf 
1210d9d501fSAlexander Graf 	return new_fdt;
1220d9d501fSAlexander Graf }
1230d9d501fSAlexander Graf 
1243eb0841bSHeinrich Schuchardt static efi_status_t efi_do_enter(
125*2074f700SHeinrich Schuchardt 			efi_handle_t image_handle, struct efi_system_table *st,
126*2074f700SHeinrich Schuchardt 			asmlinkage ulong (*entry)(efi_handle_t image_handle,
127b06d8ac3Sxypron.glpk@gmx.de 						  struct efi_system_table *st))
128b06d8ac3Sxypron.glpk@gmx.de {
129b06d8ac3Sxypron.glpk@gmx.de 	efi_status_t ret = EFI_LOAD_ERROR;
130b06d8ac3Sxypron.glpk@gmx.de 
131b06d8ac3Sxypron.glpk@gmx.de 	if (entry)
132b06d8ac3Sxypron.glpk@gmx.de 		ret = entry(image_handle, st);
133b06d8ac3Sxypron.glpk@gmx.de 	st->boottime->exit(image_handle, ret, 0, NULL);
134b06d8ac3Sxypron.glpk@gmx.de 	return ret;
135b06d8ac3Sxypron.glpk@gmx.de }
136b06d8ac3Sxypron.glpk@gmx.de 
137ec6617c3SAlison Wang #ifdef CONFIG_ARM64
1383eb0841bSHeinrich Schuchardt static efi_status_t efi_run_in_el2(asmlinkage ulong (*entry)(
139*2074f700SHeinrich Schuchardt 			efi_handle_t image_handle, struct efi_system_table *st),
140*2074f700SHeinrich Schuchardt 			efi_handle_t image_handle, struct efi_system_table *st)
141ec6617c3SAlison Wang {
142ec6617c3SAlison Wang 	/* Enable caches again */
143ec6617c3SAlison Wang 	dcache_enable();
144ec6617c3SAlison Wang 
145b06d8ac3Sxypron.glpk@gmx.de 	return efi_do_enter(image_handle, st, entry);
146ec6617c3SAlison Wang }
147ec6617c3SAlison Wang #endif
148ec6617c3SAlison Wang 
149b9939336SAlexander Graf /*
150b9939336SAlexander Graf  * Load an EFI payload into a newly allocated piece of memory, register all
151b9939336SAlexander Graf  * EFI objects it would want to access and jump to it.
152b9939336SAlexander Graf  */
1533eb0841bSHeinrich Schuchardt static efi_status_t do_bootefi_exec(void *efi, void *fdt,
15495c5553eSRob Clark 				    struct efi_device_path *device_path,
15595c5553eSRob Clark 				    struct efi_device_path *image_path)
156b9939336SAlexander Graf {
15795c5553eSRob Clark 	struct efi_loaded_image loaded_image_info = {};
15895c5553eSRob Clark 	struct efi_object loaded_image_info_obj = {};
159bf19273eSRob Clark 	struct efi_device_path *memdp = NULL;
16095c5553eSRob Clark 	ulong ret;
16195c5553eSRob Clark 
162*2074f700SHeinrich Schuchardt 	ulong (*entry)(efi_handle_t image_handle, struct efi_system_table *st)
163e275458cSSimon Glass 		asmlinkage;
164b9939336SAlexander Graf 	ulong fdt_pages, fdt_size, fdt_start, fdt_end;
165f4f9993fSAlexander Graf 	const efi_guid_t fdt_guid = EFI_FDT_GUID;
166dea2174dSAlexander Graf 	bootm_headers_t img = { 0 };
167b9939336SAlexander Graf 
168bf19273eSRob Clark 	/*
169bf19273eSRob Clark 	 * Special case for efi payload not loaded from disk, such as
170bf19273eSRob Clark 	 * 'bootefi hello' or for example payload loaded directly into
171bf19273eSRob Clark 	 * memory via jtag/etc:
172bf19273eSRob Clark 	 */
173bf19273eSRob Clark 	if (!device_path && !image_path) {
174bf19273eSRob Clark 		printf("WARNING: using memory device/image path, this may confuse some payloads!\n");
175bf19273eSRob Clark 		/* actual addresses filled in after efi_load_pe() */
176bf19273eSRob Clark 		memdp = efi_dp_from_mem(0, 0, 0);
177bf19273eSRob Clark 		device_path = image_path = memdp;
178bf19273eSRob Clark 	} else {
179bf19273eSRob Clark 		assert(device_path && image_path);
180bf19273eSRob Clark 	}
181bf19273eSRob Clark 
18295c5553eSRob Clark 	/* Initialize and populate EFI object list */
18395c5553eSRob Clark 	if (!efi_obj_list_initalized)
18495c5553eSRob Clark 		efi_init_obj_list();
18595c5553eSRob Clark 
18695c5553eSRob Clark 	efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj,
18795c5553eSRob Clark 			       device_path, image_path);
18895c5553eSRob Clark 
189b9939336SAlexander Graf 	/*
190b9939336SAlexander Graf 	 * gd lives in a fixed register which may get clobbered while we execute
191b9939336SAlexander Graf 	 * the payload. So save it here and restore it on every callback entry
192b9939336SAlexander Graf 	 */
193b9939336SAlexander Graf 	efi_save_gd();
194b9939336SAlexander Graf 
1951c39809bSAlexander Graf 	if (fdt && !fdt_check_header(fdt)) {
196dea2174dSAlexander Graf 		/* Prepare fdt for payload */
1970d9d501fSAlexander Graf 		fdt = copy_fdt(fdt);
1980d9d501fSAlexander Graf 
1990d9d501fSAlexander Graf 		if (image_setup_libfdt(&img, fdt, 0, NULL)) {
200dea2174dSAlexander Graf 			printf("ERROR: Failed to process device tree\n");
201dea2174dSAlexander Graf 			return -EINVAL;
202dea2174dSAlexander Graf 		}
203dea2174dSAlexander Graf 
204dea2174dSAlexander Graf 		/* Link to it in the efi tables */
205f4f9993fSAlexander Graf 		efi_install_configuration_table(&fdt_guid, fdt);
206b9939336SAlexander Graf 
207b9939336SAlexander Graf 		/* And reserve the space in the memory map */
2080d9d501fSAlexander Graf 		fdt_start = ((ulong)fdt) & ~EFI_PAGE_MASK;
2090d9d501fSAlexander Graf 		fdt_end = ((ulong)fdt) + fdt_totalsize(fdt);
210b9939336SAlexander Graf 		fdt_size = (fdt_end - fdt_start) + EFI_PAGE_MASK;
211b9939336SAlexander Graf 		fdt_pages = fdt_size >> EFI_PAGE_SHIFT;
212b9939336SAlexander Graf 		/* Give a bootloader the chance to modify the device tree */
213b9939336SAlexander Graf 		fdt_pages += 2;
214b9939336SAlexander Graf 		efi_add_memory_map(fdt_start, fdt_pages,
215b9939336SAlexander Graf 				   EFI_BOOT_SERVICES_DATA, true);
216b9939336SAlexander Graf 	} else {
2171c39809bSAlexander Graf 		printf("WARNING: Invalid device tree, expect boot to fail\n");
218f4f9993fSAlexander Graf 		efi_install_configuration_table(&fdt_guid, NULL);
219b9939336SAlexander Graf 	}
220b9939336SAlexander Graf 
221b57f48a8SHeinrich Schuchardt 	/* Transfer environment variable bootargs as load options */
222b57f48a8SHeinrich Schuchardt 	set_load_options(&loaded_image_info, "bootargs");
223b9939336SAlexander Graf 	/* Load the EFI payload */
224b9939336SAlexander Graf 	entry = efi_load_pe(efi, &loaded_image_info);
22595c5553eSRob Clark 	if (!entry) {
22695c5553eSRob Clark 		ret = -ENOENT;
22795c5553eSRob Clark 		goto exit;
22895c5553eSRob Clark 	}
22980a4800eSAlexander Graf 
230bf19273eSRob Clark 	if (memdp) {
231bf19273eSRob Clark 		struct efi_device_path_memory *mdp = (void *)memdp;
232bf19273eSRob Clark 		mdp->memory_type = loaded_image_info.image_code_type;
233bf19273eSRob Clark 		mdp->start_address = (uintptr_t)loaded_image_info.image_base;
234bf19273eSRob Clark 		mdp->end_address = mdp->start_address +
235bf19273eSRob Clark 				loaded_image_info.image_size;
236bf19273eSRob Clark 	}
237bf19273eSRob Clark 
238ad644e7cSRob Clark 	/* we don't support much: */
239ad644e7cSRob Clark 	env_set("efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported",
240ad644e7cSRob Clark 		"{ro,boot}(blob)0000000000000000");
241ad644e7cSRob Clark 
242b9939336SAlexander Graf 	/* Call our payload! */
243edcef3baSAlexander Graf 	debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
244a86aeaf2SAlexander Graf 
245a86aeaf2SAlexander Graf 	if (setjmp(&loaded_image_info.exit_jmp)) {
24695c5553eSRob Clark 		ret = loaded_image_info.exit_status;
24795c5553eSRob Clark 		goto exit;
248a86aeaf2SAlexander Graf 	}
249a86aeaf2SAlexander Graf 
25069bd459dSAlexander Graf #ifdef CONFIG_ARM64
25169bd459dSAlexander Graf 	/* On AArch64 we need to make sure we call our payload in < EL3 */
25269bd459dSAlexander Graf 	if (current_el() == 3) {
25369bd459dSAlexander Graf 		smp_kick_all_cpus();
25469bd459dSAlexander Graf 		dcache_disable();	/* flush cache before switch to EL2 */
255ec6617c3SAlison Wang 
256ec6617c3SAlison Wang 		/* Move into EL2 and keep running there */
257ea54ad59SHeinrich Schuchardt 		armv8_switch_to_el2((ulong)entry,
258ea54ad59SHeinrich Schuchardt 				    (ulong)&loaded_image_info_obj.handle,
2597c5e1febSAlison Wang 				    (ulong)&systab, 0, (ulong)efi_run_in_el2,
260ec6617c3SAlison Wang 				    ES_TO_AARCH64);
261ec6617c3SAlison Wang 
262ec6617c3SAlison Wang 		/* Should never reach here, efi exits with longjmp */
263ec6617c3SAlison Wang 		while (1) { }
26469bd459dSAlexander Graf 	}
26569bd459dSAlexander Graf #endif
26669bd459dSAlexander Graf 
267ea54ad59SHeinrich Schuchardt 	ret = efi_do_enter(loaded_image_info_obj.handle, &systab, entry);
26895c5553eSRob Clark 
26995c5553eSRob Clark exit:
27095c5553eSRob Clark 	/* image has returned, loaded-image obj goes *poof*: */
27195c5553eSRob Clark 	list_del(&loaded_image_info_obj.link);
27295c5553eSRob Clark 
27395c5553eSRob Clark 	return ret;
274b9939336SAlexander Graf }
275b9939336SAlexander Graf 
2769975fe96SRob Clark static int do_bootefi_bootmgr_exec(unsigned long fdt_addr)
2779975fe96SRob Clark {
2789975fe96SRob Clark 	struct efi_device_path *device_path, *file_path;
2799975fe96SRob Clark 	void *addr;
2809975fe96SRob Clark 	efi_status_t r;
2819975fe96SRob Clark 
2829975fe96SRob Clark 	/* Initialize and populate EFI object list */
2839975fe96SRob Clark 	if (!efi_obj_list_initalized)
2849975fe96SRob Clark 		efi_init_obj_list();
2859975fe96SRob Clark 
2869975fe96SRob Clark 	/*
2879975fe96SRob Clark 	 * gd lives in a fixed register which may get clobbered while we execute
2889975fe96SRob Clark 	 * the payload. So save it here and restore it on every callback entry
2899975fe96SRob Clark 	 */
2909975fe96SRob Clark 	efi_save_gd();
2919975fe96SRob Clark 
2929975fe96SRob Clark 	addr = efi_bootmgr_load(&device_path, &file_path);
2939975fe96SRob Clark 	if (!addr)
2949975fe96SRob Clark 		return 1;
2959975fe96SRob Clark 
2969975fe96SRob Clark 	printf("## Starting EFI application at %p ...\n", addr);
2979975fe96SRob Clark 	r = do_bootefi_exec(addr, (void *)fdt_addr, device_path, file_path);
2989975fe96SRob Clark 	printf("## Application terminated, r = %lu\n",
2999975fe96SRob Clark 	       r & ~EFI_ERROR_MASK);
3009975fe96SRob Clark 
3019975fe96SRob Clark 	if (r != EFI_SUCCESS)
3029975fe96SRob Clark 		return 1;
3039975fe96SRob Clark 
3049975fe96SRob Clark 	return 0;
3059975fe96SRob Clark }
3069975fe96SRob Clark 
307b9939336SAlexander Graf /* Interpreter command to boot an arbitrary EFI image from memory */
308b9939336SAlexander Graf static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
309b9939336SAlexander Graf {
3101c39809bSAlexander Graf 	char *saddr, *sfdt;
3111c39809bSAlexander Graf 	unsigned long addr, fdt_addr = 0;
3123eb0841bSHeinrich Schuchardt 	efi_status_t r;
313b9939336SAlexander Graf 
314b9939336SAlexander Graf 	if (argc < 2)
3153c1dcef6SBin Meng 		return CMD_RET_USAGE;
316c7ae3dfdSSimon Glass #ifdef CONFIG_CMD_BOOTEFI_HELLO
317c7ae3dfdSSimon Glass 	if (!strcmp(argv[1], "hello")) {
3185e44489bSHeinrich Schuchardt 		ulong size = __efi_helloworld_end - __efi_helloworld_begin;
319c7ae3dfdSSimon Glass 
32051c533fdSHeinrich Schuchardt 		saddr = env_get("loadaddr");
32151c533fdSHeinrich Schuchardt 		if (saddr)
32251c533fdSHeinrich Schuchardt 			addr = simple_strtoul(saddr, NULL, 16);
32351c533fdSHeinrich Schuchardt 		else
324c7ae3dfdSSimon Glass 			addr = CONFIG_SYS_LOAD_ADDR;
3255e44489bSHeinrich Schuchardt 		memcpy((char *)addr, __efi_helloworld_begin, size);
326c7ae3dfdSSimon Glass 	} else
327c7ae3dfdSSimon Glass #endif
328623b3a57SHeinrich Schuchardt #ifdef CONFIG_CMD_BOOTEFI_SELFTEST
329623b3a57SHeinrich Schuchardt 	if (!strcmp(argv[1], "selftest")) {
3307aca68caSHeinrich Schuchardt 		struct efi_loaded_image loaded_image_info = {};
3317aca68caSHeinrich Schuchardt 		struct efi_object loaded_image_info_obj = {};
3327aca68caSHeinrich Schuchardt 
333f972dc14SHeinrich Schuchardt 		/* Construct a dummy device path. */
334f972dc14SHeinrich Schuchardt 		bootefi_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE,
335f972dc14SHeinrich Schuchardt 						      (uintptr_t)&efi_selftest,
336f972dc14SHeinrich Schuchardt 						      (uintptr_t)&efi_selftest);
337f972dc14SHeinrich Schuchardt 		bootefi_image_path = efi_dp_from_file(NULL, 0, "\\selftest");
338f972dc14SHeinrich Schuchardt 
3397aca68caSHeinrich Schuchardt 		efi_setup_loaded_image(&loaded_image_info,
3407aca68caSHeinrich Schuchardt 				       &loaded_image_info_obj,
3417aca68caSHeinrich Schuchardt 				       bootefi_device_path, bootefi_image_path);
342623b3a57SHeinrich Schuchardt 		/*
343623b3a57SHeinrich Schuchardt 		 * gd lives in a fixed register which may get clobbered while we
344623b3a57SHeinrich Schuchardt 		 * execute the payload. So save it here and restore it on every
345623b3a57SHeinrich Schuchardt 		 * callback entry
346623b3a57SHeinrich Schuchardt 		 */
347623b3a57SHeinrich Schuchardt 		efi_save_gd();
348623b3a57SHeinrich Schuchardt 		/* Initialize and populate EFI object list */
349623b3a57SHeinrich Schuchardt 		if (!efi_obj_list_initalized)
350623b3a57SHeinrich Schuchardt 			efi_init_obj_list();
351d78e40d6SHeinrich Schuchardt 		/* Transfer environment variable efi_selftest as load options */
352d78e40d6SHeinrich Schuchardt 		set_load_options(&loaded_image_info, "efi_selftest");
353d78e40d6SHeinrich Schuchardt 		/* Execute the test */
354ea54ad59SHeinrich Schuchardt 		r = efi_selftest(loaded_image_info_obj.handle, &systab);
355c2b53902SHeinrich Schuchardt 		efi_restore_gd();
356d78e40d6SHeinrich Schuchardt 		free(loaded_image_info.load_options);
357c2b53902SHeinrich Schuchardt 		list_del(&loaded_image_info_obj.link);
358c2b53902SHeinrich Schuchardt 		return r != EFI_SUCCESS;
359623b3a57SHeinrich Schuchardt 	} else
360623b3a57SHeinrich Schuchardt #endif
3619975fe96SRob Clark 	if (!strcmp(argv[1], "bootmgr")) {
3629975fe96SRob Clark 		unsigned long fdt_addr = 0;
3639975fe96SRob Clark 
3649975fe96SRob Clark 		if (argc > 2)
3659975fe96SRob Clark 			fdt_addr = simple_strtoul(argv[2], NULL, 16);
3669975fe96SRob Clark 
3679975fe96SRob Clark 		return do_bootefi_bootmgr_exec(fdt_addr);
3689975fe96SRob Clark 	} else {
369b9939336SAlexander Graf 		saddr = argv[1];
370b9939336SAlexander Graf 
371b9939336SAlexander Graf 		addr = simple_strtoul(saddr, NULL, 16);
372b9939336SAlexander Graf 
3731c39809bSAlexander Graf 		if (argc > 2) {
3741c39809bSAlexander Graf 			sfdt = argv[2];
3751c39809bSAlexander Graf 			fdt_addr = simple_strtoul(sfdt, NULL, 16);
3761c39809bSAlexander Graf 		}
377c7ae3dfdSSimon Glass 	}
3781c39809bSAlexander Graf 
3795ee31bafSSimon Glass 	printf("## Starting EFI application at %08lx ...\n", addr);
38095c5553eSRob Clark 	r = do_bootefi_exec((void *)addr, (void *)fdt_addr,
38195c5553eSRob Clark 			    bootefi_device_path, bootefi_image_path);
3821da1bac4Sxypron.glpk@gmx.de 	printf("## Application terminated, r = %lu\n",
3831da1bac4Sxypron.glpk@gmx.de 	       r & ~EFI_ERROR_MASK);
384b9939336SAlexander Graf 
3851da1bac4Sxypron.glpk@gmx.de 	if (r != EFI_SUCCESS)
3861da1bac4Sxypron.glpk@gmx.de 		return 1;
3871da1bac4Sxypron.glpk@gmx.de 	else
3881da1bac4Sxypron.glpk@gmx.de 		return 0;
389b9939336SAlexander Graf }
390b9939336SAlexander Graf 
391b9939336SAlexander Graf #ifdef CONFIG_SYS_LONGHELP
392b9939336SAlexander Graf static char bootefi_help_text[] =
3931c39809bSAlexander Graf 	"<image address> [fdt address]\n"
3941c39809bSAlexander Graf 	"  - boot EFI payload stored at address <image address>.\n"
3951c39809bSAlexander Graf 	"    If specified, the device tree located at <fdt address> gets\n"
396c7ae3dfdSSimon Glass 	"    exposed as EFI configuration table.\n"
397c7ae3dfdSSimon Glass #ifdef CONFIG_CMD_BOOTEFI_HELLO
398623b3a57SHeinrich Schuchardt 	"bootefi hello\n"
399623b3a57SHeinrich Schuchardt 	"  - boot a sample Hello World application stored within U-Boot\n"
400623b3a57SHeinrich Schuchardt #endif
401623b3a57SHeinrich Schuchardt #ifdef CONFIG_CMD_BOOTEFI_SELFTEST
402623b3a57SHeinrich Schuchardt 	"bootefi selftest\n"
403623b3a57SHeinrich Schuchardt 	"  - boot an EFI selftest application stored within U-Boot\n"
404d78e40d6SHeinrich Schuchardt 	"    Use environment variable efi_selftest to select a single test.\n"
405d78e40d6SHeinrich Schuchardt 	"    Use 'setenv efi_selftest list' to enumerate all tests.\n"
406c7ae3dfdSSimon Glass #endif
4079975fe96SRob Clark 	"bootmgr [fdt addr]\n"
4089975fe96SRob Clark 	"  - load and boot EFI payload based on BootOrder/BootXXXX variables.\n"
4099975fe96SRob Clark 	"\n"
4109975fe96SRob Clark 	"    If specified, the device tree located at <fdt address> gets\n"
4119975fe96SRob Clark 	"    exposed as EFI configuration table.\n";
412b9939336SAlexander Graf #endif
413b9939336SAlexander Graf 
414b9939336SAlexander Graf U_BOOT_CMD(
4151c39809bSAlexander Graf 	bootefi, 3, 0, do_bootefi,
41692dfd922SSergey Kubushyn 	"Boots an EFI payload from memory",
417b9939336SAlexander Graf 	bootefi_help_text
418b9939336SAlexander Graf );
4190f4060ebSAlexander Graf 
42095c5553eSRob Clark static int parse_partnum(const char *devnr)
42195c5553eSRob Clark {
42295c5553eSRob Clark 	const char *str = strchr(devnr, ':');
42395c5553eSRob Clark 	if (str) {
42495c5553eSRob Clark 		str++;
42595c5553eSRob Clark 		return simple_strtoul(str, NULL, 16);
42695c5553eSRob Clark 	}
42795c5553eSRob Clark 	return 0;
42895c5553eSRob Clark }
42995c5553eSRob Clark 
430c07ad7c0SAlexander Graf void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
4310f4060ebSAlexander Graf {
43295c5553eSRob Clark 	char filename[32] = { 0 }; /* dp->str is u16[32] long */
43395c5553eSRob Clark 	char *s;
4340f4060ebSAlexander Graf 
43595c5553eSRob Clark 	if (strcmp(dev, "Net")) {
43695c5553eSRob Clark 		struct blk_desc *desc;
43795c5553eSRob Clark 		int part;
43895c5553eSRob Clark 
439f9d334bdSAlexander Graf 		desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10));
4408300be61SStefan Roese 		if (!desc)
4418300be61SStefan Roese 			return;
44295c5553eSRob Clark 		part = parse_partnum(devnr);
443f9d334bdSAlexander Graf 
44495c5553eSRob Clark 		bootefi_device_path = efi_dp_from_part(desc, part);
44595c5553eSRob Clark 	} else {
44695c5553eSRob Clark #ifdef CONFIG_NET
44795c5553eSRob Clark 		bootefi_device_path = efi_dp_from_eth();
448f9d334bdSAlexander Graf #endif
449f9d334bdSAlexander Graf 	}
450f9d334bdSAlexander Graf 
4519975fe96SRob Clark 	if (!path)
4529975fe96SRob Clark 		return;
4539975fe96SRob Clark 
45449271666SAlexander Graf 	if (strcmp(dev, "Net")) {
45549271666SAlexander Graf 		/* Add leading / to fs paths, because they're absolute */
45695c5553eSRob Clark 		snprintf(filename, sizeof(filename), "/%s", path);
45749271666SAlexander Graf 	} else {
45895c5553eSRob Clark 		snprintf(filename, sizeof(filename), "%s", path);
45949271666SAlexander Graf 	}
4603e433e96SRob Clark 	/* DOS style file path: */
46195c5553eSRob Clark 	s = filename;
4623e433e96SRob Clark 	while ((s = strchr(s, '/')))
4633e433e96SRob Clark 		*s++ = '\\';
46495c5553eSRob Clark 	bootefi_image_path = efi_dp_from_file(NULL, 0, filename);
4650f4060ebSAlexander Graf }
466