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
9b9939336SAlexander Graf #include <common.h>
10b9939336SAlexander Graf #include <command.h>
119d922450SSimon Glass #include <dm.h>
12b9939336SAlexander Graf #include <efi_loader.h>
13b9939336SAlexander Graf #include <errno.h>
140e00a84cSMasahiro Yamada #include <linux/libfdt.h>
150e00a84cSMasahiro Yamada #include <linux/libfdt_env.h>
16ad0c1a3dSAlexander Graf #include <memalign.h>
170d9d501fSAlexander Graf #include <asm/global_data.h>
18e275458cSSimon Glass #include <asm-generic/sections.h>
19e275458cSSimon Glass #include <linux/linkage.h>
200d9d501fSAlexander Graf
210d9d501fSAlexander Graf DECLARE_GLOBAL_DATA_PTR;
22b9939336SAlexander Graf
237cbc1241SHeinrich Schuchardt static uint8_t efi_obj_list_initalized;
247cbc1241SHeinrich Schuchardt
25*2f0750ebSRob Clark static struct efi_device_path *bootefi_image_path;
26*2f0750ebSRob Clark static struct efi_device_path *bootefi_device_path;
27b9939336SAlexander Graf
287cbc1241SHeinrich Schuchardt /* Initialize and populate EFI object list */
efi_init_obj_list(void)297cbc1241SHeinrich Schuchardt static void efi_init_obj_list(void)
307cbc1241SHeinrich Schuchardt {
317cbc1241SHeinrich Schuchardt efi_obj_list_initalized = 1;
327cbc1241SHeinrich Schuchardt
337cbc1241SHeinrich Schuchardt efi_console_register();
347cbc1241SHeinrich Schuchardt #ifdef CONFIG_PARTITIONS
357cbc1241SHeinrich Schuchardt efi_disk_register();
367cbc1241SHeinrich Schuchardt #endif
377cbc1241SHeinrich Schuchardt #if defined(CONFIG_LCD) || defined(CONFIG_DM_VIDEO)
387cbc1241SHeinrich Schuchardt efi_gop_register();
397cbc1241SHeinrich Schuchardt #endif
407cbc1241SHeinrich Schuchardt #ifdef CONFIG_NET
41*2f0750ebSRob Clark efi_net_register();
427cbc1241SHeinrich Schuchardt #endif
437cbc1241SHeinrich Schuchardt #ifdef CONFIG_GENERATE_SMBIOS_TABLE
447cbc1241SHeinrich Schuchardt efi_smbios_register();
457cbc1241SHeinrich Schuchardt #endif
467cbc1241SHeinrich Schuchardt
477cbc1241SHeinrich Schuchardt /* Initialize EFI runtime services */
487cbc1241SHeinrich Schuchardt efi_reset_system_init();
497cbc1241SHeinrich Schuchardt efi_get_time_init();
507cbc1241SHeinrich Schuchardt }
517cbc1241SHeinrich Schuchardt
copy_fdt(void * fdt)520d9d501fSAlexander Graf static void *copy_fdt(void *fdt)
530d9d501fSAlexander Graf {
540d9d501fSAlexander Graf u64 fdt_size = fdt_totalsize(fdt);
55ad0c1a3dSAlexander Graf unsigned long fdt_ram_start = -1L, fdt_pages;
56ad0c1a3dSAlexander Graf u64 new_fdt_addr;
570d9d501fSAlexander Graf void *new_fdt;
58ad0c1a3dSAlexander Graf int i;
590d9d501fSAlexander Graf
60ad0c1a3dSAlexander Graf for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
61ad0c1a3dSAlexander Graf u64 ram_start = gd->bd->bi_dram[i].start;
62ad0c1a3dSAlexander Graf u64 ram_size = gd->bd->bi_dram[i].size;
630d9d501fSAlexander Graf
64ad0c1a3dSAlexander Graf if (!ram_size)
65ad0c1a3dSAlexander Graf continue;
66ad0c1a3dSAlexander Graf
67ad0c1a3dSAlexander Graf if (ram_start < fdt_ram_start)
68ad0c1a3dSAlexander Graf fdt_ram_start = ram_start;
69ad0c1a3dSAlexander Graf }
70ad0c1a3dSAlexander Graf
71ad0c1a3dSAlexander Graf /* Give us at least 4kb breathing room */
72a44bffccSxypron.glpk@gmx.de fdt_size = ALIGN(fdt_size + 4096, EFI_PAGE_SIZE);
73ad0c1a3dSAlexander Graf fdt_pages = fdt_size >> EFI_PAGE_SHIFT;
74ad0c1a3dSAlexander Graf
75ad0c1a3dSAlexander Graf /* Safe fdt location is at 128MB */
76ad0c1a3dSAlexander Graf new_fdt_addr = fdt_ram_start + (128 * 1024 * 1024) + fdt_size;
77ad0c1a3dSAlexander Graf if (efi_allocate_pages(1, EFI_BOOT_SERVICES_DATA, fdt_pages,
78ad0c1a3dSAlexander Graf &new_fdt_addr) != EFI_SUCCESS) {
79ad0c1a3dSAlexander Graf /* If we can't put it there, put it somewhere */
80a44bffccSxypron.glpk@gmx.de new_fdt_addr = (ulong)memalign(EFI_PAGE_SIZE, fdt_size);
8185a6e9b3SAlexander Graf if (efi_allocate_pages(1, EFI_BOOT_SERVICES_DATA, fdt_pages,
8285a6e9b3SAlexander Graf &new_fdt_addr) != EFI_SUCCESS) {
8385a6e9b3SAlexander Graf printf("ERROR: Failed to reserve space for FDT\n");
8485a6e9b3SAlexander Graf return NULL;
85ad0c1a3dSAlexander Graf }
8685a6e9b3SAlexander Graf }
8785a6e9b3SAlexander Graf
88ad0c1a3dSAlexander Graf new_fdt = (void*)(ulong)new_fdt_addr;
890d9d501fSAlexander Graf memcpy(new_fdt, fdt, fdt_totalsize(fdt));
900d9d501fSAlexander Graf fdt_set_totalsize(new_fdt, fdt_size);
910d9d501fSAlexander Graf
920d9d501fSAlexander Graf return new_fdt;
930d9d501fSAlexander Graf }
940d9d501fSAlexander Graf
efi_do_enter(void * image_handle,struct efi_system_table * st,asmlinkage ulong (* entry)(void * image_handle,struct efi_system_table * st))95b06d8ac3Sxypron.glpk@gmx.de static ulong efi_do_enter(void *image_handle,
96b06d8ac3Sxypron.glpk@gmx.de struct efi_system_table *st,
97b06d8ac3Sxypron.glpk@gmx.de asmlinkage ulong (*entry)(void *image_handle,
98b06d8ac3Sxypron.glpk@gmx.de struct efi_system_table *st))
99b06d8ac3Sxypron.glpk@gmx.de {
100b06d8ac3Sxypron.glpk@gmx.de efi_status_t ret = EFI_LOAD_ERROR;
101b06d8ac3Sxypron.glpk@gmx.de
102b06d8ac3Sxypron.glpk@gmx.de if (entry)
103b06d8ac3Sxypron.glpk@gmx.de ret = entry(image_handle, st);
104b06d8ac3Sxypron.glpk@gmx.de st->boottime->exit(image_handle, ret, 0, NULL);
105b06d8ac3Sxypron.glpk@gmx.de return ret;
106b06d8ac3Sxypron.glpk@gmx.de }
107b06d8ac3Sxypron.glpk@gmx.de
108ec6617c3SAlison Wang #ifdef CONFIG_ARM64
efi_run_in_el2(asmlinkage ulong (* entry)(void * image_handle,struct efi_system_table * st),void * image_handle,struct efi_system_table * st)109b06d8ac3Sxypron.glpk@gmx.de static unsigned long efi_run_in_el2(asmlinkage ulong (*entry)(
110b06d8ac3Sxypron.glpk@gmx.de void *image_handle, struct efi_system_table *st),
111b06d8ac3Sxypron.glpk@gmx.de void *image_handle, struct efi_system_table *st)
112ec6617c3SAlison Wang {
113ec6617c3SAlison Wang /* Enable caches again */
114ec6617c3SAlison Wang dcache_enable();
115ec6617c3SAlison Wang
116b06d8ac3Sxypron.glpk@gmx.de return efi_do_enter(image_handle, st, entry);
117ec6617c3SAlison Wang }
118ec6617c3SAlison Wang #endif
119ec6617c3SAlison Wang
120b9939336SAlexander Graf /*
121b9939336SAlexander Graf * Load an EFI payload into a newly allocated piece of memory, register all
122b9939336SAlexander Graf * EFI objects it would want to access and jump to it.
123b9939336SAlexander Graf */
do_bootefi_exec(void * efi,void * fdt,struct efi_device_path * device_path,struct efi_device_path * image_path)124*2f0750ebSRob Clark static unsigned long do_bootefi_exec(void *efi, void *fdt,
125*2f0750ebSRob Clark struct efi_device_path *device_path,
126*2f0750ebSRob Clark struct efi_device_path *image_path)
127b9939336SAlexander Graf {
128*2f0750ebSRob Clark struct efi_loaded_image loaded_image_info = {};
129*2f0750ebSRob Clark struct efi_object loaded_image_info_obj = {};
130*2f0750ebSRob Clark ulong ret;
131*2f0750ebSRob Clark
132e275458cSSimon Glass ulong (*entry)(void *image_handle, struct efi_system_table *st)
133e275458cSSimon Glass asmlinkage;
134b9939336SAlexander Graf ulong fdt_pages, fdt_size, fdt_start, fdt_end;
135f4f9993fSAlexander Graf const efi_guid_t fdt_guid = EFI_FDT_GUID;
136dea2174dSAlexander Graf bootm_headers_t img = { 0 };
137b9939336SAlexander Graf
138*2f0750ebSRob Clark /* Initialize and populate EFI object list */
139*2f0750ebSRob Clark if (!efi_obj_list_initalized)
140*2f0750ebSRob Clark efi_init_obj_list();
141*2f0750ebSRob Clark
142*2f0750ebSRob Clark efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj,
143*2f0750ebSRob Clark device_path, image_path);
144*2f0750ebSRob Clark
145b9939336SAlexander Graf /*
146b9939336SAlexander Graf * gd lives in a fixed register which may get clobbered while we execute
147b9939336SAlexander Graf * the payload. So save it here and restore it on every callback entry
148b9939336SAlexander Graf */
149b9939336SAlexander Graf efi_save_gd();
150b9939336SAlexander Graf
1511c39809bSAlexander Graf if (fdt && !fdt_check_header(fdt)) {
152dea2174dSAlexander Graf /* Prepare fdt for payload */
1530d9d501fSAlexander Graf fdt = copy_fdt(fdt);
1540d9d501fSAlexander Graf
1550d9d501fSAlexander Graf if (image_setup_libfdt(&img, fdt, 0, NULL)) {
156dea2174dSAlexander Graf printf("ERROR: Failed to process device tree\n");
157dea2174dSAlexander Graf return -EINVAL;
158dea2174dSAlexander Graf }
159dea2174dSAlexander Graf
160dea2174dSAlexander Graf /* Link to it in the efi tables */
161f4f9993fSAlexander Graf efi_install_configuration_table(&fdt_guid, fdt);
162b9939336SAlexander Graf
163b9939336SAlexander Graf /* And reserve the space in the memory map */
1640d9d501fSAlexander Graf fdt_start = ((ulong)fdt) & ~EFI_PAGE_MASK;
1650d9d501fSAlexander Graf fdt_end = ((ulong)fdt) + fdt_totalsize(fdt);
166b9939336SAlexander Graf fdt_size = (fdt_end - fdt_start) + EFI_PAGE_MASK;
167b9939336SAlexander Graf fdt_pages = fdt_size >> EFI_PAGE_SHIFT;
168b9939336SAlexander Graf /* Give a bootloader the chance to modify the device tree */
169b9939336SAlexander Graf fdt_pages += 2;
170b9939336SAlexander Graf efi_add_memory_map(fdt_start, fdt_pages,
171b9939336SAlexander Graf EFI_BOOT_SERVICES_DATA, true);
172b9939336SAlexander Graf } else {
1731c39809bSAlexander Graf printf("WARNING: Invalid device tree, expect boot to fail\n");
174f4f9993fSAlexander Graf efi_install_configuration_table(&fdt_guid, NULL);
175b9939336SAlexander Graf }
176b9939336SAlexander Graf
177b9939336SAlexander Graf /* Load the EFI payload */
178b9939336SAlexander Graf entry = efi_load_pe(efi, &loaded_image_info);
179*2f0750ebSRob Clark if (!entry) {
180*2f0750ebSRob Clark ret = -ENOENT;
181*2f0750ebSRob Clark goto exit;
182*2f0750ebSRob Clark }
18380a4800eSAlexander Graf
184b9939336SAlexander Graf /* Call our payload! */
185edcef3baSAlexander Graf debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
186a86aeaf2SAlexander Graf
187a86aeaf2SAlexander Graf if (setjmp(&loaded_image_info.exit_jmp)) {
188*2f0750ebSRob Clark ret = loaded_image_info.exit_status;
189*2f0750ebSRob Clark EFI_EXIT(ret);
190*2f0750ebSRob Clark goto exit;
191a86aeaf2SAlexander Graf }
192a86aeaf2SAlexander Graf
19369bd459dSAlexander Graf #ifdef CONFIG_ARM64
19469bd459dSAlexander Graf /* On AArch64 we need to make sure we call our payload in < EL3 */
19569bd459dSAlexander Graf if (current_el() == 3) {
19669bd459dSAlexander Graf smp_kick_all_cpus();
19769bd459dSAlexander Graf dcache_disable(); /* flush cache before switch to EL2 */
198ec6617c3SAlison Wang
199ec6617c3SAlison Wang /* Move into EL2 and keep running there */
200ec6617c3SAlison Wang armv8_switch_to_el2((ulong)entry, (ulong)&loaded_image_info,
2017c5e1febSAlison Wang (ulong)&systab, 0, (ulong)efi_run_in_el2,
202ec6617c3SAlison Wang ES_TO_AARCH64);
203ec6617c3SAlison Wang
204ec6617c3SAlison Wang /* Should never reach here, efi exits with longjmp */
205ec6617c3SAlison Wang while (1) { }
20669bd459dSAlexander Graf }
20769bd459dSAlexander Graf #endif
20869bd459dSAlexander Graf
209*2f0750ebSRob Clark ret = efi_do_enter(&loaded_image_info, &systab, entry);
210*2f0750ebSRob Clark
211*2f0750ebSRob Clark exit:
212*2f0750ebSRob Clark /* image has returned, loaded-image obj goes *poof*: */
213*2f0750ebSRob Clark list_del(&loaded_image_info_obj.link);
214*2f0750ebSRob Clark
215*2f0750ebSRob Clark return ret;
216b9939336SAlexander Graf }
217b9939336SAlexander Graf
218b9939336SAlexander Graf
219b9939336SAlexander Graf /* Interpreter command to boot an arbitrary EFI image from memory */
do_bootefi(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])220b9939336SAlexander Graf static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
221b9939336SAlexander Graf {
2221c39809bSAlexander Graf char *saddr, *sfdt;
2231c39809bSAlexander Graf unsigned long addr, fdt_addr = 0;
2241da1bac4Sxypron.glpk@gmx.de unsigned long r;
225b9939336SAlexander Graf
226b9939336SAlexander Graf if (argc < 2)
2273c1dcef6SBin Meng return CMD_RET_USAGE;
228c7ae3dfdSSimon Glass #ifdef CONFIG_CMD_BOOTEFI_HELLO
229c7ae3dfdSSimon Glass if (!strcmp(argv[1], "hello")) {
230c7ae3dfdSSimon Glass ulong size = __efi_hello_world_end - __efi_hello_world_begin;
231c7ae3dfdSSimon Glass
23210eca968SHeinrich Schuchardt saddr = env_get("loadaddr");
23310eca968SHeinrich Schuchardt if (saddr)
23410eca968SHeinrich Schuchardt addr = simple_strtoul(saddr, NULL, 16);
23510eca968SHeinrich Schuchardt else
236c7ae3dfdSSimon Glass addr = CONFIG_SYS_LOAD_ADDR;
237c7ae3dfdSSimon Glass memcpy((char *)addr, __efi_hello_world_begin, size);
238c7ae3dfdSSimon Glass } else
239c7ae3dfdSSimon Glass #endif
240c7ae3dfdSSimon Glass {
241b9939336SAlexander Graf saddr = argv[1];
242b9939336SAlexander Graf
243b9939336SAlexander Graf addr = simple_strtoul(saddr, NULL, 16);
244b9939336SAlexander Graf
2451c39809bSAlexander Graf if (argc > 2) {
2461c39809bSAlexander Graf sfdt = argv[2];
2471c39809bSAlexander Graf fdt_addr = simple_strtoul(sfdt, NULL, 16);
2481c39809bSAlexander Graf }
249c7ae3dfdSSimon Glass }
2501c39809bSAlexander Graf
2515ee31bafSSimon Glass printf("## Starting EFI application at %08lx ...\n", addr);
252*2f0750ebSRob Clark r = do_bootefi_exec((void *)addr, (void *)fdt_addr,
253*2f0750ebSRob Clark bootefi_device_path, bootefi_image_path);
2541da1bac4Sxypron.glpk@gmx.de printf("## Application terminated, r = %lu\n",
2551da1bac4Sxypron.glpk@gmx.de r & ~EFI_ERROR_MASK);
256b9939336SAlexander Graf
2571da1bac4Sxypron.glpk@gmx.de if (r != EFI_SUCCESS)
2581da1bac4Sxypron.glpk@gmx.de return 1;
2591da1bac4Sxypron.glpk@gmx.de else
2601da1bac4Sxypron.glpk@gmx.de return 0;
261b9939336SAlexander Graf }
262b9939336SAlexander Graf
263b9939336SAlexander Graf #ifdef CONFIG_SYS_LONGHELP
264b9939336SAlexander Graf static char bootefi_help_text[] =
2651c39809bSAlexander Graf "<image address> [fdt address]\n"
2661c39809bSAlexander Graf " - boot EFI payload stored at address <image address>.\n"
2671c39809bSAlexander Graf " If specified, the device tree located at <fdt address> gets\n"
268c7ae3dfdSSimon Glass " exposed as EFI configuration table.\n"
269c7ae3dfdSSimon Glass #ifdef CONFIG_CMD_BOOTEFI_HELLO
270c7ae3dfdSSimon Glass "hello\n"
271c7ae3dfdSSimon Glass " - boot a sample Hello World application stored within U-Boot"
272c7ae3dfdSSimon Glass #endif
273c7ae3dfdSSimon Glass ;
274b9939336SAlexander Graf #endif
275b9939336SAlexander Graf
276b9939336SAlexander Graf U_BOOT_CMD(
2771c39809bSAlexander Graf bootefi, 3, 0, do_bootefi,
27892dfd922SSergey Kubushyn "Boots an EFI payload from memory",
279b9939336SAlexander Graf bootefi_help_text
280b9939336SAlexander Graf );
2810f4060ebSAlexander Graf
parse_partnum(const char * devnr)282*2f0750ebSRob Clark static int parse_partnum(const char *devnr)
283*2f0750ebSRob Clark {
284*2f0750ebSRob Clark const char *str = strchr(devnr, ':');
285*2f0750ebSRob Clark if (str) {
286*2f0750ebSRob Clark str++;
287*2f0750ebSRob Clark return simple_strtoul(str, NULL, 16);
288*2f0750ebSRob Clark }
289*2f0750ebSRob Clark return 0;
290*2f0750ebSRob Clark }
291*2f0750ebSRob Clark
efi_set_bootdev(const char * dev,const char * devnr,const char * path)292c07ad7c0SAlexander Graf void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
2930f4060ebSAlexander Graf {
294*2f0750ebSRob Clark char filename[32] = { 0 }; /* dp->str is u16[32] long */
295*2f0750ebSRob Clark char *s;
2960f4060ebSAlexander Graf
297*2f0750ebSRob Clark if (strcmp(dev, "Net")) {
298*2f0750ebSRob Clark struct blk_desc *desc;
299*2f0750ebSRob Clark int part;
300*2f0750ebSRob Clark
301f9d334bdSAlexander Graf desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10));
302*2f0750ebSRob Clark part = parse_partnum(devnr);
303f9d334bdSAlexander Graf
304*2f0750ebSRob Clark bootefi_device_path = efi_dp_from_part(desc, part);
305*2f0750ebSRob Clark } else {
306*2f0750ebSRob Clark #ifdef CONFIG_NET
307*2f0750ebSRob Clark bootefi_device_path = efi_dp_from_eth();
308f9d334bdSAlexander Graf #endif
309f9d334bdSAlexander Graf }
310f9d334bdSAlexander Graf
31149271666SAlexander Graf if (strcmp(dev, "Net")) {
31249271666SAlexander Graf /* Add leading / to fs paths, because they're absolute */
313*2f0750ebSRob Clark snprintf(filename, sizeof(filename), "/%s", path);
31449271666SAlexander Graf } else {
315*2f0750ebSRob Clark snprintf(filename, sizeof(filename), "%s", path);
31649271666SAlexander Graf }
3173e433e96SRob Clark /* DOS style file path: */
318*2f0750ebSRob Clark s = filename;
3193e433e96SRob Clark while ((s = strchr(s, '/')))
3203e433e96SRob Clark *s++ = '\\';
321*2f0750ebSRob Clark bootefi_image_path = efi_dp_from_file(NULL, 0, filename);
3220f4060ebSAlexander Graf }
323