xref: /openbmc/u-boot/cmd/bootefi.c (revision 58bc69d2)
1f739fcd8STom Rini // SPDX-License-Identifier: GPL-2.0+
2b9939336SAlexander Graf /*
3b9939336SAlexander Graf  *  EFI application loader
4b9939336SAlexander Graf  *
5b9939336SAlexander Graf  *  Copyright (c) 2016 Alexander Graf
6b9939336SAlexander Graf  */
7b9939336SAlexander Graf 
8d78e40d6SHeinrich Schuchardt #include <charset.h>
9b9939336SAlexander Graf #include <common.h>
10b9939336SAlexander Graf #include <command.h>
119d922450SSimon Glass #include <dm.h>
12b9939336SAlexander Graf #include <efi_loader.h>
13d78e40d6SHeinrich Schuchardt #include <efi_selftest.h>
14b9939336SAlexander Graf #include <errno.h>
15b08c8c48SMasahiro Yamada #include <linux/libfdt.h>
16b08c8c48SMasahiro Yamada #include <linux/libfdt_env.h>
17ad0c1a3dSAlexander Graf #include <memalign.h>
180d9d501fSAlexander Graf #include <asm/global_data.h>
19e275458cSSimon Glass #include <asm-generic/sections.h>
20c3b11deaSHeinrich Schuchardt #include <asm-generic/unaligned.h>
21e275458cSSimon Glass #include <linux/linkage.h>
220d9d501fSAlexander Graf 
230d9d501fSAlexander Graf DECLARE_GLOBAL_DATA_PTR;
24b9939336SAlexander Graf 
25fc225e60SHeinrich Schuchardt #define OBJ_LIST_NOT_INITIALIZED 1
26fc225e60SHeinrich Schuchardt 
27fc225e60SHeinrich Schuchardt static efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED;
287cbc1241SHeinrich Schuchardt 
2995c5553eSRob Clark static struct efi_device_path *bootefi_image_path;
3095c5553eSRob Clark static struct efi_device_path *bootefi_device_path;
31b9939336SAlexander Graf 
327cbc1241SHeinrich Schuchardt /* Initialize and populate EFI object list */
33fc225e60SHeinrich Schuchardt efi_status_t efi_init_obj_list(void)
347cbc1241SHeinrich Schuchardt {
35fc225e60SHeinrich Schuchardt 	efi_status_t ret = EFI_SUCCESS;
36fc225e60SHeinrich Schuchardt 
37098a6cddSHeinrich Schuchardt 	/* Initialize once only */
38fc225e60SHeinrich Schuchardt 	if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED)
39fc225e60SHeinrich Schuchardt 		return efi_obj_list_initialized;
407cbc1241SHeinrich Schuchardt 
4105ef48a2SHeinrich Schuchardt 	/* Initialize EFI driver uclass */
42fc225e60SHeinrich Schuchardt 	ret = efi_driver_init();
43fc225e60SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
44fc225e60SHeinrich Schuchardt 		goto out;
4505ef48a2SHeinrich Schuchardt 
46fc225e60SHeinrich Schuchardt 	ret = efi_console_register();
47fc225e60SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
48fc225e60SHeinrich Schuchardt 		goto out;
497cbc1241SHeinrich Schuchardt #ifdef CONFIG_PARTITIONS
50fc225e60SHeinrich Schuchardt 	ret = efi_disk_register();
51fc225e60SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
52fc225e60SHeinrich Schuchardt 		goto out;
537cbc1241SHeinrich Schuchardt #endif
547cbc1241SHeinrich Schuchardt #if defined(CONFIG_LCD) || defined(CONFIG_DM_VIDEO)
55fc225e60SHeinrich Schuchardt 	ret = efi_gop_register();
56fc225e60SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
57fc225e60SHeinrich Schuchardt 		goto out;
587cbc1241SHeinrich Schuchardt #endif
59092f2f35SJoe Hershberger #ifdef CONFIG_NET
60fc225e60SHeinrich Schuchardt 	ret = efi_net_register();
61fc225e60SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
62fc225e60SHeinrich Schuchardt 		goto out;
637cbc1241SHeinrich Schuchardt #endif
647cbc1241SHeinrich Schuchardt #ifdef CONFIG_GENERATE_SMBIOS_TABLE
65fc225e60SHeinrich Schuchardt 	ret = efi_smbios_register();
66fc225e60SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
67fc225e60SHeinrich Schuchardt 		goto out;
687cbc1241SHeinrich Schuchardt #endif
69fc225e60SHeinrich Schuchardt 	ret = efi_watchdog_register();
70fc225e60SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
71fc225e60SHeinrich Schuchardt 		goto out;
727cbc1241SHeinrich Schuchardt 
737cbc1241SHeinrich Schuchardt 	/* Initialize EFI runtime services */
74fc225e60SHeinrich Schuchardt 	ret = efi_reset_system_init();
75fc225e60SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
76fc225e60SHeinrich Schuchardt 		goto out;
77fc225e60SHeinrich Schuchardt 	ret = efi_get_time_init();
78fc225e60SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
79fc225e60SHeinrich Schuchardt 		goto out;
80fc225e60SHeinrich Schuchardt 
81fc225e60SHeinrich Schuchardt out:
82fc225e60SHeinrich Schuchardt 	efi_obj_list_initialized = ret;
83fc225e60SHeinrich Schuchardt 	return ret;
847cbc1241SHeinrich Schuchardt }
857cbc1241SHeinrich Schuchardt 
86d78e40d6SHeinrich Schuchardt /*
87c3b11deaSHeinrich Schuchardt  * Allow unaligned memory access.
88c3b11deaSHeinrich Schuchardt  *
89c3b11deaSHeinrich Schuchardt  * This routine is overridden by architectures providing this feature.
90c3b11deaSHeinrich Schuchardt  */
91c3b11deaSHeinrich Schuchardt void __weak allow_unaligned(void)
92c3b11deaSHeinrich Schuchardt {
93c3b11deaSHeinrich Schuchardt }
94c3b11deaSHeinrich Schuchardt 
95c3b11deaSHeinrich Schuchardt /*
96d78e40d6SHeinrich Schuchardt  * Set the load options of an image from an environment variable.
97d78e40d6SHeinrich Schuchardt  *
98d78e40d6SHeinrich Schuchardt  * @loaded_image_info:	the image
99d78e40d6SHeinrich Schuchardt  * @env_var:		name of the environment variable
100d78e40d6SHeinrich Schuchardt  */
101d78e40d6SHeinrich Schuchardt static void set_load_options(struct efi_loaded_image *loaded_image_info,
102d78e40d6SHeinrich Schuchardt 			     const char *env_var)
103d78e40d6SHeinrich Schuchardt {
104d78e40d6SHeinrich Schuchardt 	size_t size;
105d78e40d6SHeinrich Schuchardt 	const char *env = env_get(env_var);
106d78e40d6SHeinrich Schuchardt 
107d78e40d6SHeinrich Schuchardt 	loaded_image_info->load_options = NULL;
108d78e40d6SHeinrich Schuchardt 	loaded_image_info->load_options_size = 0;
109d78e40d6SHeinrich Schuchardt 	if (!env)
110d78e40d6SHeinrich Schuchardt 		return;
111d78e40d6SHeinrich Schuchardt 	size = strlen(env) + 1;
112d78e40d6SHeinrich Schuchardt 	loaded_image_info->load_options = calloc(size, sizeof(u16));
113d78e40d6SHeinrich Schuchardt 	if (!loaded_image_info->load_options) {
114d78e40d6SHeinrich Schuchardt 		printf("ERROR: Out of memory\n");
115d78e40d6SHeinrich Schuchardt 		return;
116d78e40d6SHeinrich Schuchardt 	}
117d78e40d6SHeinrich Schuchardt 	utf8_to_utf16(loaded_image_info->load_options, (u8 *)env, size);
118d78e40d6SHeinrich Schuchardt 	loaded_image_info->load_options_size = size * 2;
119d78e40d6SHeinrich Schuchardt }
120d78e40d6SHeinrich Schuchardt 
1210d9d501fSAlexander Graf static void *copy_fdt(void *fdt)
1220d9d501fSAlexander Graf {
1230d9d501fSAlexander Graf 	u64 fdt_size = fdt_totalsize(fdt);
124ad0c1a3dSAlexander Graf 	unsigned long fdt_ram_start = -1L, fdt_pages;
125ad0c1a3dSAlexander Graf 	u64 new_fdt_addr;
1260d9d501fSAlexander Graf 	void *new_fdt;
127ad0c1a3dSAlexander Graf 	int i;
1280d9d501fSAlexander Graf 
129ad0c1a3dSAlexander Graf         for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
130ad0c1a3dSAlexander Graf                 u64 ram_start = gd->bd->bi_dram[i].start;
131ad0c1a3dSAlexander Graf                 u64 ram_size = gd->bd->bi_dram[i].size;
1320d9d501fSAlexander Graf 
133ad0c1a3dSAlexander Graf 		if (!ram_size)
134ad0c1a3dSAlexander Graf 			continue;
135ad0c1a3dSAlexander Graf 
136ad0c1a3dSAlexander Graf 		if (ram_start < fdt_ram_start)
137ad0c1a3dSAlexander Graf 			fdt_ram_start = ram_start;
138ad0c1a3dSAlexander Graf 	}
139ad0c1a3dSAlexander Graf 
140ad0c1a3dSAlexander Graf 	/* Give us at least 4kb breathing room */
141a44bffccSxypron.glpk@gmx.de 	fdt_size = ALIGN(fdt_size + 4096, EFI_PAGE_SIZE);
142ad0c1a3dSAlexander Graf 	fdt_pages = fdt_size >> EFI_PAGE_SHIFT;
143ad0c1a3dSAlexander Graf 
144ad0c1a3dSAlexander Graf 	/* Safe fdt location is at 128MB */
145ad0c1a3dSAlexander Graf 	new_fdt_addr = fdt_ram_start + (128 * 1024 * 1024) + fdt_size;
146e09159c8SHeinrich Schuchardt 	if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
147e09159c8SHeinrich Schuchardt 			       EFI_RUNTIME_SERVICES_DATA, fdt_pages,
148ad0c1a3dSAlexander Graf 			       &new_fdt_addr) != EFI_SUCCESS) {
149ad0c1a3dSAlexander Graf 		/* If we can't put it there, put it somewhere */
150a44bffccSxypron.glpk@gmx.de 		new_fdt_addr = (ulong)memalign(EFI_PAGE_SIZE, fdt_size);
151e09159c8SHeinrich Schuchardt 		if (efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
152e09159c8SHeinrich Schuchardt 				       EFI_RUNTIME_SERVICES_DATA, fdt_pages,
15385a6e9b3SAlexander Graf 				       &new_fdt_addr) != EFI_SUCCESS) {
15485a6e9b3SAlexander Graf 			printf("ERROR: Failed to reserve space for FDT\n");
15585a6e9b3SAlexander Graf 			return NULL;
156ad0c1a3dSAlexander Graf 		}
15785a6e9b3SAlexander Graf 	}
15885a6e9b3SAlexander Graf 
159ad0c1a3dSAlexander Graf 	new_fdt = (void*)(ulong)new_fdt_addr;
1600d9d501fSAlexander Graf 	memcpy(new_fdt, fdt, fdt_totalsize(fdt));
1610d9d501fSAlexander Graf 	fdt_set_totalsize(new_fdt, fdt_size);
1620d9d501fSAlexander Graf 
1630d9d501fSAlexander Graf 	return new_fdt;
1640d9d501fSAlexander Graf }
1650d9d501fSAlexander Graf 
1663eb0841bSHeinrich Schuchardt static efi_status_t efi_do_enter(
1672074f700SHeinrich Schuchardt 			efi_handle_t image_handle, struct efi_system_table *st,
168c6fa5df6SAlexander Graf 			EFIAPI efi_status_t (*entry)(
169c6fa5df6SAlexander Graf 				efi_handle_t image_handle,
170b06d8ac3Sxypron.glpk@gmx.de 				struct efi_system_table *st))
171b06d8ac3Sxypron.glpk@gmx.de {
172b06d8ac3Sxypron.glpk@gmx.de 	efi_status_t ret = EFI_LOAD_ERROR;
173b06d8ac3Sxypron.glpk@gmx.de 
174b06d8ac3Sxypron.glpk@gmx.de 	if (entry)
175b06d8ac3Sxypron.glpk@gmx.de 		ret = entry(image_handle, st);
176b06d8ac3Sxypron.glpk@gmx.de 	st->boottime->exit(image_handle, ret, 0, NULL);
177b06d8ac3Sxypron.glpk@gmx.de 	return ret;
178b06d8ac3Sxypron.glpk@gmx.de }
179b06d8ac3Sxypron.glpk@gmx.de 
180ec6617c3SAlison Wang #ifdef CONFIG_ARM64
181c6fa5df6SAlexander Graf static efi_status_t efi_run_in_el2(EFIAPI efi_status_t (*entry)(
1822074f700SHeinrich Schuchardt 			efi_handle_t image_handle, struct efi_system_table *st),
1832074f700SHeinrich Schuchardt 			efi_handle_t image_handle, struct efi_system_table *st)
184ec6617c3SAlison Wang {
185ec6617c3SAlison Wang 	/* Enable caches again */
186ec6617c3SAlison Wang 	dcache_enable();
187ec6617c3SAlison Wang 
188b06d8ac3Sxypron.glpk@gmx.de 	return efi_do_enter(image_handle, st, entry);
189ec6617c3SAlison Wang }
190ec6617c3SAlison Wang #endif
191ec6617c3SAlison Wang 
192806d2fa8SAlexander Graf /* Carve out DT reserved memory ranges */
193806d2fa8SAlexander Graf static efi_status_t efi_carve_out_dt_rsv(void *fdt)
194806d2fa8SAlexander Graf {
195806d2fa8SAlexander Graf 	int nr_rsv, i;
196806d2fa8SAlexander Graf 	uint64_t addr, size, pages;
197806d2fa8SAlexander Graf 
198806d2fa8SAlexander Graf 	nr_rsv = fdt_num_mem_rsv(fdt);
199806d2fa8SAlexander Graf 
200806d2fa8SAlexander Graf 	/* Look for an existing entry and add it to the efi mem map. */
201806d2fa8SAlexander Graf 	for (i = 0; i < nr_rsv; i++) {
202806d2fa8SAlexander Graf 		if (fdt_get_mem_rsv(fdt, i, &addr, &size) != 0)
203806d2fa8SAlexander Graf 			continue;
204806d2fa8SAlexander Graf 
205806d2fa8SAlexander Graf 		pages = ALIGN(size, EFI_PAGE_SIZE) >> EFI_PAGE_SHIFT;
206806d2fa8SAlexander Graf 		efi_add_memory_map(addr, pages, EFI_RESERVED_MEMORY_TYPE,
207806d2fa8SAlexander Graf 				   false);
208806d2fa8SAlexander Graf 	}
209806d2fa8SAlexander Graf 
210806d2fa8SAlexander Graf 	return EFI_SUCCESS;
211806d2fa8SAlexander Graf }
212806d2fa8SAlexander Graf 
213bc4f9133SHeinrich Schuchardt static efi_status_t efi_install_fdt(void *fdt)
214bc4f9133SHeinrich Schuchardt {
215bc4f9133SHeinrich Schuchardt 	bootm_headers_t img = { 0 };
216bc4f9133SHeinrich Schuchardt 	ulong fdt_pages, fdt_size, fdt_start, fdt_end;
217bc4f9133SHeinrich Schuchardt 	efi_status_t ret;
218bc4f9133SHeinrich Schuchardt 
219bc4f9133SHeinrich Schuchardt 	if (fdt_check_header(fdt)) {
220bc4f9133SHeinrich Schuchardt 		printf("ERROR: invalid device tree\n");
221bc4f9133SHeinrich Schuchardt 		return EFI_INVALID_PARAMETER;
222bc4f9133SHeinrich Schuchardt 	}
223bc4f9133SHeinrich Schuchardt 
224bc4f9133SHeinrich Schuchardt 	/* Prepare fdt for payload */
225bc4f9133SHeinrich Schuchardt 	fdt = copy_fdt(fdt);
226bc4f9133SHeinrich Schuchardt 	if (!fdt)
227bc4f9133SHeinrich Schuchardt 		return EFI_OUT_OF_RESOURCES;
228bc4f9133SHeinrich Schuchardt 
229bc4f9133SHeinrich Schuchardt 	if (image_setup_libfdt(&img, fdt, 0, NULL)) {
230bc4f9133SHeinrich Schuchardt 		printf("ERROR: failed to process device tree\n");
231bc4f9133SHeinrich Schuchardt 		return EFI_LOAD_ERROR;
232bc4f9133SHeinrich Schuchardt 	}
233bc4f9133SHeinrich Schuchardt 
234806d2fa8SAlexander Graf 	if (efi_carve_out_dt_rsv(fdt) != EFI_SUCCESS) {
235806d2fa8SAlexander Graf 		printf("ERROR: failed to carve out memory\n");
236806d2fa8SAlexander Graf 		return EFI_LOAD_ERROR;
237806d2fa8SAlexander Graf 	}
238806d2fa8SAlexander Graf 
239bc4f9133SHeinrich Schuchardt 	/* Link to it in the efi tables */
240bc4f9133SHeinrich Schuchardt 	ret = efi_install_configuration_table(&efi_guid_fdt, fdt);
241bc4f9133SHeinrich Schuchardt 	if (ret != EFI_SUCCESS)
242bc4f9133SHeinrich Schuchardt 		return EFI_OUT_OF_RESOURCES;
243bc4f9133SHeinrich Schuchardt 
244bc4f9133SHeinrich Schuchardt 	/* And reserve the space in the memory map */
245bc4f9133SHeinrich Schuchardt 	fdt_start = ((ulong)fdt) & ~EFI_PAGE_MASK;
246bc4f9133SHeinrich Schuchardt 	fdt_end = ((ulong)fdt) + fdt_totalsize(fdt);
247bc4f9133SHeinrich Schuchardt 	fdt_size = (fdt_end - fdt_start) + EFI_PAGE_MASK;
248bc4f9133SHeinrich Schuchardt 	fdt_pages = fdt_size >> EFI_PAGE_SHIFT;
249bc4f9133SHeinrich Schuchardt 	/* Give a bootloader the chance to modify the device tree */
250bc4f9133SHeinrich Schuchardt 	fdt_pages += 2;
251bc4f9133SHeinrich Schuchardt 	ret = efi_add_memory_map(fdt_start, fdt_pages,
252bc4f9133SHeinrich Schuchardt 				 EFI_BOOT_SERVICES_DATA, true);
253bc4f9133SHeinrich Schuchardt 	return ret;
254bc4f9133SHeinrich Schuchardt }
255bc4f9133SHeinrich Schuchardt 
256b9939336SAlexander Graf /*
257b9939336SAlexander Graf  * Load an EFI payload into a newly allocated piece of memory, register all
258b9939336SAlexander Graf  * EFI objects it would want to access and jump to it.
259b9939336SAlexander Graf  */
260bc4f9133SHeinrich Schuchardt static efi_status_t do_bootefi_exec(void *efi,
26195c5553eSRob Clark 				    struct efi_device_path *device_path,
26295c5553eSRob Clark 				    struct efi_device_path *image_path)
263b9939336SAlexander Graf {
26495c5553eSRob Clark 	struct efi_loaded_image loaded_image_info = {};
26595c5553eSRob Clark 	struct efi_object loaded_image_info_obj = {};
266*58bc69d2SAlexander Graf 	struct efi_object mem_obj = {};
267bf19273eSRob Clark 	struct efi_device_path *memdp = NULL;
26845204b10SHeinrich Schuchardt 	efi_status_t ret;
26995c5553eSRob Clark 
270c6fa5df6SAlexander Graf 	EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
271c6fa5df6SAlexander Graf 				     struct efi_system_table *st);
272b9939336SAlexander Graf 
273bf19273eSRob Clark 	/*
274bf19273eSRob Clark 	 * Special case for efi payload not loaded from disk, such as
275bf19273eSRob Clark 	 * 'bootefi hello' or for example payload loaded directly into
276bf19273eSRob Clark 	 * memory via jtag/etc:
277bf19273eSRob Clark 	 */
278bf19273eSRob Clark 	if (!device_path && !image_path) {
279bf19273eSRob Clark 		printf("WARNING: using memory device/image path, this may confuse some payloads!\n");
280bf19273eSRob Clark 		/* actual addresses filled in after efi_load_pe() */
281bf19273eSRob Clark 		memdp = efi_dp_from_mem(0, 0, 0);
282bf19273eSRob Clark 		device_path = image_path = memdp;
283*58bc69d2SAlexander Graf 		efi_add_handle(&mem_obj);
284*58bc69d2SAlexander Graf 
285*58bc69d2SAlexander Graf 		ret = efi_add_protocol(mem_obj.handle, &efi_guid_device_path,
286*58bc69d2SAlexander Graf 				       device_path);
287*58bc69d2SAlexander Graf 		if (ret != EFI_SUCCESS)
288*58bc69d2SAlexander Graf 			goto exit;
289bf19273eSRob Clark 	} else {
290bf19273eSRob Clark 		assert(device_path && image_path);
291bf19273eSRob Clark 	}
292bf19273eSRob Clark 
29395c5553eSRob Clark 	efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj,
29495c5553eSRob Clark 			       device_path, image_path);
29595c5553eSRob Clark 
296b9939336SAlexander Graf 	/*
297b9939336SAlexander Graf 	 * gd lives in a fixed register which may get clobbered while we execute
298b9939336SAlexander Graf 	 * the payload. So save it here and restore it on every callback entry
299b9939336SAlexander Graf 	 */
300b9939336SAlexander Graf 	efi_save_gd();
301b9939336SAlexander Graf 
302b57f48a8SHeinrich Schuchardt 	/* Transfer environment variable bootargs as load options */
303b57f48a8SHeinrich Schuchardt 	set_load_options(&loaded_image_info, "bootargs");
304b9939336SAlexander Graf 	/* Load the EFI payload */
305b9939336SAlexander Graf 	entry = efi_load_pe(efi, &loaded_image_info);
30695c5553eSRob Clark 	if (!entry) {
30745204b10SHeinrich Schuchardt 		ret = EFI_LOAD_ERROR;
30895c5553eSRob Clark 		goto exit;
30995c5553eSRob Clark 	}
31080a4800eSAlexander Graf 
311bf19273eSRob Clark 	if (memdp) {
312bf19273eSRob Clark 		struct efi_device_path_memory *mdp = (void *)memdp;
313bf19273eSRob Clark 		mdp->memory_type = loaded_image_info.image_code_type;
314bf19273eSRob Clark 		mdp->start_address = (uintptr_t)loaded_image_info.image_base;
315bf19273eSRob Clark 		mdp->end_address = mdp->start_address +
316bf19273eSRob Clark 				loaded_image_info.image_size;
317bf19273eSRob Clark 	}
318bf19273eSRob Clark 
319ad644e7cSRob Clark 	/* we don't support much: */
320ad644e7cSRob Clark 	env_set("efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported",
321ad644e7cSRob Clark 		"{ro,boot}(blob)0000000000000000");
322ad644e7cSRob Clark 
323b9939336SAlexander Graf 	/* Call our payload! */
324edcef3baSAlexander Graf 	debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
325a86aeaf2SAlexander Graf 
326a86aeaf2SAlexander Graf 	if (setjmp(&loaded_image_info.exit_jmp)) {
32795c5553eSRob Clark 		ret = loaded_image_info.exit_status;
32895c5553eSRob Clark 		goto exit;
329a86aeaf2SAlexander Graf 	}
330a86aeaf2SAlexander Graf 
33169bd459dSAlexander Graf #ifdef CONFIG_ARM64
33269bd459dSAlexander Graf 	/* On AArch64 we need to make sure we call our payload in < EL3 */
33369bd459dSAlexander Graf 	if (current_el() == 3) {
33469bd459dSAlexander Graf 		smp_kick_all_cpus();
33569bd459dSAlexander Graf 		dcache_disable();	/* flush cache before switch to EL2 */
336ec6617c3SAlison Wang 
337ec6617c3SAlison Wang 		/* Move into EL2 and keep running there */
338ea54ad59SHeinrich Schuchardt 		armv8_switch_to_el2((ulong)entry,
339ea54ad59SHeinrich Schuchardt 				    (ulong)&loaded_image_info_obj.handle,
3407c5e1febSAlison Wang 				    (ulong)&systab, 0, (ulong)efi_run_in_el2,
341ec6617c3SAlison Wang 				    ES_TO_AARCH64);
342ec6617c3SAlison Wang 
343ec6617c3SAlison Wang 		/* Should never reach here, efi exits with longjmp */
344ec6617c3SAlison Wang 		while (1) { }
34569bd459dSAlexander Graf 	}
34669bd459dSAlexander Graf #endif
34769bd459dSAlexander Graf 
348ea54ad59SHeinrich Schuchardt 	ret = efi_do_enter(loaded_image_info_obj.handle, &systab, entry);
34995c5553eSRob Clark 
35095c5553eSRob Clark exit:
35195c5553eSRob Clark 	/* image has returned, loaded-image obj goes *poof*: */
35295c5553eSRob Clark 	list_del(&loaded_image_info_obj.link);
353*58bc69d2SAlexander Graf 	if (mem_obj.handle)
354*58bc69d2SAlexander Graf 		list_del(&mem_obj.link);
35595c5553eSRob Clark 
35695c5553eSRob Clark 	return ret;
357b9939336SAlexander Graf }
358b9939336SAlexander Graf 
359bc4f9133SHeinrich Schuchardt static int do_bootefi_bootmgr_exec(void)
3609975fe96SRob Clark {
3619975fe96SRob Clark 	struct efi_device_path *device_path, *file_path;
3629975fe96SRob Clark 	void *addr;
3639975fe96SRob Clark 	efi_status_t r;
3649975fe96SRob Clark 
3659975fe96SRob Clark 	/*
3669975fe96SRob Clark 	 * gd lives in a fixed register which may get clobbered while we execute
3679975fe96SRob Clark 	 * the payload. So save it here and restore it on every callback entry
3689975fe96SRob Clark 	 */
3699975fe96SRob Clark 	efi_save_gd();
3709975fe96SRob Clark 
3719975fe96SRob Clark 	addr = efi_bootmgr_load(&device_path, &file_path);
3729975fe96SRob Clark 	if (!addr)
3739975fe96SRob Clark 		return 1;
3749975fe96SRob Clark 
3759975fe96SRob Clark 	printf("## Starting EFI application at %p ...\n", addr);
376bc4f9133SHeinrich Schuchardt 	r = do_bootefi_exec(addr, device_path, file_path);
3779975fe96SRob Clark 	printf("## Application terminated, r = %lu\n",
3789975fe96SRob Clark 	       r & ~EFI_ERROR_MASK);
3799975fe96SRob Clark 
3809975fe96SRob Clark 	if (r != EFI_SUCCESS)
3819975fe96SRob Clark 		return 1;
3829975fe96SRob Clark 
3839975fe96SRob Clark 	return 0;
3849975fe96SRob Clark }
3859975fe96SRob Clark 
386b9939336SAlexander Graf /* Interpreter command to boot an arbitrary EFI image from memory */
387b9939336SAlexander Graf static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
388b9939336SAlexander Graf {
389bc4f9133SHeinrich Schuchardt 	unsigned long addr;
390bc4f9133SHeinrich Schuchardt 	char *saddr;
3913eb0841bSHeinrich Schuchardt 	efi_status_t r;
392bc4f9133SHeinrich Schuchardt 	void *fdt_addr;
393b9939336SAlexander Graf 
394c3b11deaSHeinrich Schuchardt 	/* Allow unaligned memory access */
395c3b11deaSHeinrich Schuchardt 	allow_unaligned();
396c3b11deaSHeinrich Schuchardt 
397fc225e60SHeinrich Schuchardt 	/* Initialize EFI drivers */
398fc225e60SHeinrich Schuchardt 	r = efi_init_obj_list();
399fc225e60SHeinrich Schuchardt 	if (r != EFI_SUCCESS) {
400fc225e60SHeinrich Schuchardt 		printf("Error: Cannot set up EFI drivers, r = %lu\n",
401fc225e60SHeinrich Schuchardt 		       r & ~EFI_ERROR_MASK);
402fc225e60SHeinrich Schuchardt 		return CMD_RET_FAILURE;
403fc225e60SHeinrich Schuchardt 	}
404fc225e60SHeinrich Schuchardt 
405b9939336SAlexander Graf 	if (argc < 2)
4063c1dcef6SBin Meng 		return CMD_RET_USAGE;
407bc4f9133SHeinrich Schuchardt 
408bc4f9133SHeinrich Schuchardt 	if (argc > 2) {
409bc4f9133SHeinrich Schuchardt 		fdt_addr = (void *)simple_strtoul(argv[2], NULL, 16);
410bc4f9133SHeinrich Schuchardt 		if (!fdt_addr && *argv[2] != '0')
411bc4f9133SHeinrich Schuchardt 			return CMD_RET_USAGE;
412bc4f9133SHeinrich Schuchardt 		/* Install device tree */
413bc4f9133SHeinrich Schuchardt 		r = efi_install_fdt(fdt_addr);
414bc4f9133SHeinrich Schuchardt 		if (r != EFI_SUCCESS) {
415bc4f9133SHeinrich Schuchardt 			printf("ERROR: failed to install device tree\n");
416bc4f9133SHeinrich Schuchardt 			return CMD_RET_FAILURE;
417bc4f9133SHeinrich Schuchardt 		}
418bc4f9133SHeinrich Schuchardt 	} else {
419bc4f9133SHeinrich Schuchardt 		/* Remove device tree. EFI_NOT_FOUND can be ignored here */
420bc4f9133SHeinrich Schuchardt 		efi_install_configuration_table(&efi_guid_fdt, NULL);
421bc4f9133SHeinrich Schuchardt 		printf("WARNING: booting without device tree\n");
422bc4f9133SHeinrich Schuchardt 	}
423c7ae3dfdSSimon Glass #ifdef CONFIG_CMD_BOOTEFI_HELLO
424c7ae3dfdSSimon Glass 	if (!strcmp(argv[1], "hello")) {
4255e44489bSHeinrich Schuchardt 		ulong size = __efi_helloworld_end - __efi_helloworld_begin;
426c7ae3dfdSSimon Glass 
42751c533fdSHeinrich Schuchardt 		saddr = env_get("loadaddr");
42851c533fdSHeinrich Schuchardt 		if (saddr)
42951c533fdSHeinrich Schuchardt 			addr = simple_strtoul(saddr, NULL, 16);
43051c533fdSHeinrich Schuchardt 		else
431c7ae3dfdSSimon Glass 			addr = CONFIG_SYS_LOAD_ADDR;
4325e44489bSHeinrich Schuchardt 		memcpy((char *)addr, __efi_helloworld_begin, size);
433c7ae3dfdSSimon Glass 	} else
434c7ae3dfdSSimon Glass #endif
435623b3a57SHeinrich Schuchardt #ifdef CONFIG_CMD_BOOTEFI_SELFTEST
436623b3a57SHeinrich Schuchardt 	if (!strcmp(argv[1], "selftest")) {
4377aca68caSHeinrich Schuchardt 		struct efi_loaded_image loaded_image_info = {};
4387aca68caSHeinrich Schuchardt 		struct efi_object loaded_image_info_obj = {};
4397aca68caSHeinrich Schuchardt 
440f972dc14SHeinrich Schuchardt 		/* Construct a dummy device path. */
441f972dc14SHeinrich Schuchardt 		bootefi_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE,
442f972dc14SHeinrich Schuchardt 						      (uintptr_t)&efi_selftest,
443f972dc14SHeinrich Schuchardt 						      (uintptr_t)&efi_selftest);
444f972dc14SHeinrich Schuchardt 		bootefi_image_path = efi_dp_from_file(NULL, 0, "\\selftest");
445f972dc14SHeinrich Schuchardt 
4467aca68caSHeinrich Schuchardt 		efi_setup_loaded_image(&loaded_image_info,
4477aca68caSHeinrich Schuchardt 				       &loaded_image_info_obj,
4487aca68caSHeinrich Schuchardt 				       bootefi_device_path, bootefi_image_path);
449623b3a57SHeinrich Schuchardt 		/*
450623b3a57SHeinrich Schuchardt 		 * gd lives in a fixed register which may get clobbered while we
451623b3a57SHeinrich Schuchardt 		 * execute the payload. So save it here and restore it on every
452623b3a57SHeinrich Schuchardt 		 * callback entry
453623b3a57SHeinrich Schuchardt 		 */
454623b3a57SHeinrich Schuchardt 		efi_save_gd();
455d78e40d6SHeinrich Schuchardt 		/* Transfer environment variable efi_selftest as load options */
456d78e40d6SHeinrich Schuchardt 		set_load_options(&loaded_image_info, "efi_selftest");
457d78e40d6SHeinrich Schuchardt 		/* Execute the test */
458ea54ad59SHeinrich Schuchardt 		r = efi_selftest(loaded_image_info_obj.handle, &systab);
459c2b53902SHeinrich Schuchardt 		efi_restore_gd();
460d78e40d6SHeinrich Schuchardt 		free(loaded_image_info.load_options);
461c2b53902SHeinrich Schuchardt 		list_del(&loaded_image_info_obj.link);
462c2b53902SHeinrich Schuchardt 		return r != EFI_SUCCESS;
463623b3a57SHeinrich Schuchardt 	} else
464623b3a57SHeinrich Schuchardt #endif
4659975fe96SRob Clark 	if (!strcmp(argv[1], "bootmgr")) {
466bc4f9133SHeinrich Schuchardt 		return do_bootefi_bootmgr_exec();
4679975fe96SRob Clark 	} else {
468b9939336SAlexander Graf 		saddr = argv[1];
469b9939336SAlexander Graf 
470b9939336SAlexander Graf 		addr = simple_strtoul(saddr, NULL, 16);
47149db1cb8SHeinrich Schuchardt 		/* Check that a numeric value was passed */
47249db1cb8SHeinrich Schuchardt 		if (!addr && *saddr != '0')
47349db1cb8SHeinrich Schuchardt 			return CMD_RET_USAGE;
474b9939336SAlexander Graf 
475c7ae3dfdSSimon Glass 	}
4761c39809bSAlexander Graf 
4775ee31bafSSimon Glass 	printf("## Starting EFI application at %08lx ...\n", addr);
478bc4f9133SHeinrich Schuchardt 	r = do_bootefi_exec((void *)addr, bootefi_device_path,
479bc4f9133SHeinrich Schuchardt 			    bootefi_image_path);
4801da1bac4Sxypron.glpk@gmx.de 	printf("## Application terminated, r = %lu\n",
4811da1bac4Sxypron.glpk@gmx.de 	       r & ~EFI_ERROR_MASK);
482b9939336SAlexander Graf 
4831da1bac4Sxypron.glpk@gmx.de 	if (r != EFI_SUCCESS)
4841da1bac4Sxypron.glpk@gmx.de 		return 1;
4851da1bac4Sxypron.glpk@gmx.de 	else
4861da1bac4Sxypron.glpk@gmx.de 		return 0;
487b9939336SAlexander Graf }
488b9939336SAlexander Graf 
489b9939336SAlexander Graf #ifdef CONFIG_SYS_LONGHELP
490b9939336SAlexander Graf static char bootefi_help_text[] =
4911c39809bSAlexander Graf 	"<image address> [fdt address]\n"
4921c39809bSAlexander Graf 	"  - boot EFI payload stored at address <image address>.\n"
4931c39809bSAlexander Graf 	"    If specified, the device tree located at <fdt address> gets\n"
494c7ae3dfdSSimon Glass 	"    exposed as EFI configuration table.\n"
495c7ae3dfdSSimon Glass #ifdef CONFIG_CMD_BOOTEFI_HELLO
496623b3a57SHeinrich Schuchardt 	"bootefi hello\n"
497623b3a57SHeinrich Schuchardt 	"  - boot a sample Hello World application stored within U-Boot\n"
498623b3a57SHeinrich Schuchardt #endif
499623b3a57SHeinrich Schuchardt #ifdef CONFIG_CMD_BOOTEFI_SELFTEST
500bc4f9133SHeinrich Schuchardt 	"bootefi selftest [fdt address]\n"
501623b3a57SHeinrich Schuchardt 	"  - boot an EFI selftest application stored within U-Boot\n"
502d78e40d6SHeinrich Schuchardt 	"    Use environment variable efi_selftest to select a single test.\n"
503d78e40d6SHeinrich Schuchardt 	"    Use 'setenv efi_selftest list' to enumerate all tests.\n"
504c7ae3dfdSSimon Glass #endif
505f623e07fSHeinrich Schuchardt 	"bootefi bootmgr [fdt addr]\n"
5069975fe96SRob Clark 	"  - load and boot EFI payload based on BootOrder/BootXXXX variables.\n"
5079975fe96SRob Clark 	"\n"
5089975fe96SRob Clark 	"    If specified, the device tree located at <fdt address> gets\n"
5099975fe96SRob Clark 	"    exposed as EFI configuration table.\n";
510b9939336SAlexander Graf #endif
511b9939336SAlexander Graf 
512b9939336SAlexander Graf U_BOOT_CMD(
5131c39809bSAlexander Graf 	bootefi, 3, 0, do_bootefi,
51492dfd922SSergey Kubushyn 	"Boots an EFI payload from memory",
515b9939336SAlexander Graf 	bootefi_help_text
516b9939336SAlexander Graf );
5170f4060ebSAlexander Graf 
518c07ad7c0SAlexander Graf void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
5190f4060ebSAlexander Graf {
52095c5553eSRob Clark 	char filename[32] = { 0 }; /* dp->str is u16[32] long */
52195c5553eSRob Clark 	char *s;
5220f4060ebSAlexander Graf 
52395c5553eSRob Clark 	if (strcmp(dev, "Net")) {
52495c5553eSRob Clark 		struct blk_desc *desc;
5252db1eba1SHeinrich Schuchardt 		disk_partition_t fs_partition;
52695c5553eSRob Clark 		int part;
52795c5553eSRob Clark 
5282db1eba1SHeinrich Schuchardt 		part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition,
5292db1eba1SHeinrich Schuchardt 					       1);
5302db1eba1SHeinrich Schuchardt 		if (part < 0)
5318300be61SStefan Roese 			return;
532f9d334bdSAlexander Graf 
53395c5553eSRob Clark 		bootefi_device_path = efi_dp_from_part(desc, part);
53495c5553eSRob Clark 	} else {
535092f2f35SJoe Hershberger #ifdef CONFIG_NET
53695c5553eSRob Clark 		bootefi_device_path = efi_dp_from_eth();
537f9d334bdSAlexander Graf #endif
538f9d334bdSAlexander Graf 	}
539f9d334bdSAlexander Graf 
5409975fe96SRob Clark 	if (!path)
5419975fe96SRob Clark 		return;
5429975fe96SRob Clark 
54349271666SAlexander Graf 	if (strcmp(dev, "Net")) {
54449271666SAlexander Graf 		/* Add leading / to fs paths, because they're absolute */
54595c5553eSRob Clark 		snprintf(filename, sizeof(filename), "/%s", path);
54649271666SAlexander Graf 	} else {
54795c5553eSRob Clark 		snprintf(filename, sizeof(filename), "%s", path);
54849271666SAlexander Graf 	}
5493e433e96SRob Clark 	/* DOS style file path: */
55095c5553eSRob Clark 	s = filename;
5513e433e96SRob Clark 	while ((s = strchr(s, '/')))
5523e433e96SRob Clark 		*s++ = '\\';
55395c5553eSRob Clark 	bootefi_image_path = efi_dp_from_file(NULL, 0, filename);
5540f4060ebSAlexander Graf }
555