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> 14b9939336SAlexander Graf #include <libfdt.h> 15b9939336SAlexander Graf #include <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*95c5553eSRob Clark static struct efi_device_path *bootefi_image_path; 26*95c5553eSRob Clark static struct efi_device_path *bootefi_device_path; 27b9939336SAlexander Graf 287cbc1241SHeinrich Schuchardt /* Initialize and populate EFI object list */ 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*95c5553eSRob 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 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 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 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 */ 124*95c5553eSRob Clark static unsigned long do_bootefi_exec(void *efi, void *fdt, 125*95c5553eSRob Clark struct efi_device_path *device_path, 126*95c5553eSRob Clark struct efi_device_path *image_path) 127b9939336SAlexander Graf { 128*95c5553eSRob Clark struct efi_loaded_image loaded_image_info = {}; 129*95c5553eSRob Clark struct efi_object loaded_image_info_obj = {}; 130*95c5553eSRob Clark ulong ret; 131*95c5553eSRob 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*95c5553eSRob Clark /* Initialize and populate EFI object list */ 139*95c5553eSRob Clark if (!efi_obj_list_initalized) 140*95c5553eSRob Clark efi_init_obj_list(); 141*95c5553eSRob Clark 142*95c5553eSRob Clark efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj, 143*95c5553eSRob Clark device_path, image_path); 144*95c5553eSRob 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*95c5553eSRob Clark if (!entry) { 180*95c5553eSRob Clark ret = -ENOENT; 181*95c5553eSRob Clark goto exit; 182*95c5553eSRob 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*95c5553eSRob Clark ret = loaded_image_info.exit_status; 189*95c5553eSRob Clark EFI_EXIT(ret); 190*95c5553eSRob 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*95c5553eSRob Clark ret = efi_do_enter(&loaded_image_info, &systab, entry); 210*95c5553eSRob Clark 211*95c5553eSRob Clark exit: 212*95c5553eSRob Clark /* image has returned, loaded-image obj goes *poof*: */ 213*95c5553eSRob Clark list_del(&loaded_image_info_obj.link); 214*95c5553eSRob Clark 215*95c5553eSRob Clark return ret; 216b9939336SAlexander Graf } 217b9939336SAlexander Graf 218b9939336SAlexander Graf /* Interpreter command to boot an arbitrary EFI image from memory */ 219b9939336SAlexander Graf static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 220b9939336SAlexander Graf { 2211c39809bSAlexander Graf char *saddr, *sfdt; 2221c39809bSAlexander Graf unsigned long addr, fdt_addr = 0; 2231da1bac4Sxypron.glpk@gmx.de unsigned long r; 224b9939336SAlexander Graf 225b9939336SAlexander Graf if (argc < 2) 2263c1dcef6SBin Meng return CMD_RET_USAGE; 227c7ae3dfdSSimon Glass #ifdef CONFIG_CMD_BOOTEFI_HELLO 228c7ae3dfdSSimon Glass if (!strcmp(argv[1], "hello")) { 2295e44489bSHeinrich Schuchardt ulong size = __efi_helloworld_end - __efi_helloworld_begin; 230c7ae3dfdSSimon Glass 23151c533fdSHeinrich Schuchardt saddr = env_get("loadaddr"); 23251c533fdSHeinrich Schuchardt if (saddr) 23351c533fdSHeinrich Schuchardt addr = simple_strtoul(saddr, NULL, 16); 23451c533fdSHeinrich Schuchardt else 235c7ae3dfdSSimon Glass addr = CONFIG_SYS_LOAD_ADDR; 2365e44489bSHeinrich Schuchardt memcpy((char *)addr, __efi_helloworld_begin, size); 237c7ae3dfdSSimon Glass } else 238c7ae3dfdSSimon Glass #endif 239623b3a57SHeinrich Schuchardt #ifdef CONFIG_CMD_BOOTEFI_SELFTEST 240623b3a57SHeinrich Schuchardt if (!strcmp(argv[1], "selftest")) { 241623b3a57SHeinrich Schuchardt /* 242623b3a57SHeinrich Schuchardt * gd lives in a fixed register which may get clobbered while we 243623b3a57SHeinrich Schuchardt * execute the payload. So save it here and restore it on every 244623b3a57SHeinrich Schuchardt * callback entry 245623b3a57SHeinrich Schuchardt */ 246623b3a57SHeinrich Schuchardt efi_save_gd(); 247623b3a57SHeinrich Schuchardt /* Initialize and populate EFI object list */ 248623b3a57SHeinrich Schuchardt if (!efi_obj_list_initalized) 249623b3a57SHeinrich Schuchardt efi_init_obj_list(); 250623b3a57SHeinrich Schuchardt loaded_image_info.device_handle = bootefi_device_path; 251623b3a57SHeinrich Schuchardt loaded_image_info.file_path = bootefi_image_path; 252623b3a57SHeinrich Schuchardt return efi_selftest(&loaded_image_info, &systab); 253623b3a57SHeinrich Schuchardt } else 254623b3a57SHeinrich Schuchardt #endif 255c7ae3dfdSSimon Glass { 256b9939336SAlexander Graf saddr = argv[1]; 257b9939336SAlexander Graf 258b9939336SAlexander Graf addr = simple_strtoul(saddr, NULL, 16); 259b9939336SAlexander Graf 2601c39809bSAlexander Graf if (argc > 2) { 2611c39809bSAlexander Graf sfdt = argv[2]; 2621c39809bSAlexander Graf fdt_addr = simple_strtoul(sfdt, NULL, 16); 2631c39809bSAlexander Graf } 264c7ae3dfdSSimon Glass } 2651c39809bSAlexander Graf 2665ee31bafSSimon Glass printf("## Starting EFI application at %08lx ...\n", addr); 267*95c5553eSRob Clark r = do_bootefi_exec((void *)addr, (void *)fdt_addr, 268*95c5553eSRob Clark bootefi_device_path, bootefi_image_path); 2691da1bac4Sxypron.glpk@gmx.de printf("## Application terminated, r = %lu\n", 2701da1bac4Sxypron.glpk@gmx.de r & ~EFI_ERROR_MASK); 271b9939336SAlexander Graf 2721da1bac4Sxypron.glpk@gmx.de if (r != EFI_SUCCESS) 2731da1bac4Sxypron.glpk@gmx.de return 1; 2741da1bac4Sxypron.glpk@gmx.de else 2751da1bac4Sxypron.glpk@gmx.de return 0; 276b9939336SAlexander Graf } 277b9939336SAlexander Graf 278b9939336SAlexander Graf #ifdef CONFIG_SYS_LONGHELP 279b9939336SAlexander Graf static char bootefi_help_text[] = 2801c39809bSAlexander Graf "<image address> [fdt address]\n" 2811c39809bSAlexander Graf " - boot EFI payload stored at address <image address>.\n" 2821c39809bSAlexander Graf " If specified, the device tree located at <fdt address> gets\n" 283c7ae3dfdSSimon Glass " exposed as EFI configuration table.\n" 284c7ae3dfdSSimon Glass #ifdef CONFIG_CMD_BOOTEFI_HELLO 285623b3a57SHeinrich Schuchardt "bootefi hello\n" 286623b3a57SHeinrich Schuchardt " - boot a sample Hello World application stored within U-Boot\n" 287623b3a57SHeinrich Schuchardt #endif 288623b3a57SHeinrich Schuchardt #ifdef CONFIG_CMD_BOOTEFI_SELFTEST 289623b3a57SHeinrich Schuchardt "bootefi selftest\n" 290623b3a57SHeinrich Schuchardt " - boot an EFI selftest application stored within U-Boot\n" 291c7ae3dfdSSimon Glass #endif 292c7ae3dfdSSimon Glass ; 293b9939336SAlexander Graf #endif 294b9939336SAlexander Graf 295b9939336SAlexander Graf U_BOOT_CMD( 2961c39809bSAlexander Graf bootefi, 3, 0, do_bootefi, 29792dfd922SSergey Kubushyn "Boots an EFI payload from memory", 298b9939336SAlexander Graf bootefi_help_text 299b9939336SAlexander Graf ); 3000f4060ebSAlexander Graf 301*95c5553eSRob Clark static int parse_partnum(const char *devnr) 302*95c5553eSRob Clark { 303*95c5553eSRob Clark const char *str = strchr(devnr, ':'); 304*95c5553eSRob Clark if (str) { 305*95c5553eSRob Clark str++; 306*95c5553eSRob Clark return simple_strtoul(str, NULL, 16); 307*95c5553eSRob Clark } 308*95c5553eSRob Clark return 0; 309*95c5553eSRob Clark } 310*95c5553eSRob Clark 311c07ad7c0SAlexander Graf void efi_set_bootdev(const char *dev, const char *devnr, const char *path) 3120f4060ebSAlexander Graf { 313*95c5553eSRob Clark char filename[32] = { 0 }; /* dp->str is u16[32] long */ 314*95c5553eSRob Clark char *s; 3150f4060ebSAlexander Graf 316*95c5553eSRob Clark if (strcmp(dev, "Net")) { 317*95c5553eSRob Clark struct blk_desc *desc; 318*95c5553eSRob Clark int part; 319*95c5553eSRob Clark 320f9d334bdSAlexander Graf desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10)); 321*95c5553eSRob Clark part = parse_partnum(devnr); 322f9d334bdSAlexander Graf 323*95c5553eSRob Clark bootefi_device_path = efi_dp_from_part(desc, part); 324*95c5553eSRob Clark } else { 325*95c5553eSRob Clark #ifdef CONFIG_NET 326*95c5553eSRob Clark bootefi_device_path = efi_dp_from_eth(); 327f9d334bdSAlexander Graf #endif 328f9d334bdSAlexander Graf } 329f9d334bdSAlexander Graf 33049271666SAlexander Graf if (strcmp(dev, "Net")) { 33149271666SAlexander Graf /* Add leading / to fs paths, because they're absolute */ 332*95c5553eSRob Clark snprintf(filename, sizeof(filename), "/%s", path); 33349271666SAlexander Graf } else { 334*95c5553eSRob Clark snprintf(filename, sizeof(filename), "%s", path); 33549271666SAlexander Graf } 3363e433e96SRob Clark /* DOS style file path: */ 337*95c5553eSRob Clark s = filename; 3383e433e96SRob Clark while ((s = strchr(s, '/'))) 3393e433e96SRob Clark *s++ = '\\'; 340*95c5553eSRob Clark bootefi_image_path = efi_dp_from_file(NULL, 0, filename); 3410f4060ebSAlexander Graf } 342