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 2595c5553eSRob Clark static struct efi_device_path *bootefi_image_path; 2695c5553eSRob 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 4195c5553eSRob 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 */ 12495c5553eSRob Clark static unsigned long do_bootefi_exec(void *efi, void *fdt, 12595c5553eSRob Clark struct efi_device_path *device_path, 12695c5553eSRob Clark struct efi_device_path *image_path) 127b9939336SAlexander Graf { 12895c5553eSRob Clark struct efi_loaded_image loaded_image_info = {}; 12995c5553eSRob Clark struct efi_object loaded_image_info_obj = {}; 13095c5553eSRob Clark ulong ret; 13195c5553eSRob 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 13895c5553eSRob Clark /* Initialize and populate EFI object list */ 13995c5553eSRob Clark if (!efi_obj_list_initalized) 14095c5553eSRob Clark efi_init_obj_list(); 14195c5553eSRob Clark 14295c5553eSRob Clark efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj, 14395c5553eSRob Clark device_path, image_path); 14495c5553eSRob 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); 17995c5553eSRob Clark if (!entry) { 18095c5553eSRob Clark ret = -ENOENT; 18195c5553eSRob Clark goto exit; 18295c5553eSRob Clark } 18380a4800eSAlexander Graf 184*ad644e7cSRob Clark /* we don't support much: */ 185*ad644e7cSRob Clark env_set("efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported", 186*ad644e7cSRob Clark "{ro,boot}(blob)0000000000000000"); 187*ad644e7cSRob Clark 188b9939336SAlexander Graf /* Call our payload! */ 189edcef3baSAlexander Graf debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry); 190a86aeaf2SAlexander Graf 191a86aeaf2SAlexander Graf if (setjmp(&loaded_image_info.exit_jmp)) { 19295c5553eSRob Clark ret = loaded_image_info.exit_status; 19395c5553eSRob Clark EFI_EXIT(ret); 19495c5553eSRob Clark goto exit; 195a86aeaf2SAlexander Graf } 196a86aeaf2SAlexander Graf 19769bd459dSAlexander Graf #ifdef CONFIG_ARM64 19869bd459dSAlexander Graf /* On AArch64 we need to make sure we call our payload in < EL3 */ 19969bd459dSAlexander Graf if (current_el() == 3) { 20069bd459dSAlexander Graf smp_kick_all_cpus(); 20169bd459dSAlexander Graf dcache_disable(); /* flush cache before switch to EL2 */ 202ec6617c3SAlison Wang 203ec6617c3SAlison Wang /* Move into EL2 and keep running there */ 204ec6617c3SAlison Wang armv8_switch_to_el2((ulong)entry, (ulong)&loaded_image_info, 2057c5e1febSAlison Wang (ulong)&systab, 0, (ulong)efi_run_in_el2, 206ec6617c3SAlison Wang ES_TO_AARCH64); 207ec6617c3SAlison Wang 208ec6617c3SAlison Wang /* Should never reach here, efi exits with longjmp */ 209ec6617c3SAlison Wang while (1) { } 21069bd459dSAlexander Graf } 21169bd459dSAlexander Graf #endif 21269bd459dSAlexander Graf 21395c5553eSRob Clark ret = efi_do_enter(&loaded_image_info, &systab, entry); 21495c5553eSRob Clark 21595c5553eSRob Clark exit: 21695c5553eSRob Clark /* image has returned, loaded-image obj goes *poof*: */ 21795c5553eSRob Clark list_del(&loaded_image_info_obj.link); 21895c5553eSRob Clark 21995c5553eSRob Clark return ret; 220b9939336SAlexander Graf } 221b9939336SAlexander Graf 222b9939336SAlexander Graf /* Interpreter command to boot an arbitrary EFI image from memory */ 223b9939336SAlexander Graf static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 224b9939336SAlexander Graf { 2251c39809bSAlexander Graf char *saddr, *sfdt; 2261c39809bSAlexander Graf unsigned long addr, fdt_addr = 0; 2271da1bac4Sxypron.glpk@gmx.de unsigned long r; 228b9939336SAlexander Graf 229b9939336SAlexander Graf if (argc < 2) 2303c1dcef6SBin Meng return CMD_RET_USAGE; 231c7ae3dfdSSimon Glass #ifdef CONFIG_CMD_BOOTEFI_HELLO 232c7ae3dfdSSimon Glass if (!strcmp(argv[1], "hello")) { 2335e44489bSHeinrich Schuchardt ulong size = __efi_helloworld_end - __efi_helloworld_begin; 234c7ae3dfdSSimon Glass 23551c533fdSHeinrich Schuchardt saddr = env_get("loadaddr"); 23651c533fdSHeinrich Schuchardt if (saddr) 23751c533fdSHeinrich Schuchardt addr = simple_strtoul(saddr, NULL, 16); 23851c533fdSHeinrich Schuchardt else 239c7ae3dfdSSimon Glass addr = CONFIG_SYS_LOAD_ADDR; 2405e44489bSHeinrich Schuchardt memcpy((char *)addr, __efi_helloworld_begin, size); 241c7ae3dfdSSimon Glass } else 242c7ae3dfdSSimon Glass #endif 243623b3a57SHeinrich Schuchardt #ifdef CONFIG_CMD_BOOTEFI_SELFTEST 244623b3a57SHeinrich Schuchardt if (!strcmp(argv[1], "selftest")) { 245623b3a57SHeinrich Schuchardt /* 246623b3a57SHeinrich Schuchardt * gd lives in a fixed register which may get clobbered while we 247623b3a57SHeinrich Schuchardt * execute the payload. So save it here and restore it on every 248623b3a57SHeinrich Schuchardt * callback entry 249623b3a57SHeinrich Schuchardt */ 250623b3a57SHeinrich Schuchardt efi_save_gd(); 251623b3a57SHeinrich Schuchardt /* Initialize and populate EFI object list */ 252623b3a57SHeinrich Schuchardt if (!efi_obj_list_initalized) 253623b3a57SHeinrich Schuchardt efi_init_obj_list(); 254623b3a57SHeinrich Schuchardt loaded_image_info.device_handle = bootefi_device_path; 255623b3a57SHeinrich Schuchardt loaded_image_info.file_path = bootefi_image_path; 256623b3a57SHeinrich Schuchardt return efi_selftest(&loaded_image_info, &systab); 257623b3a57SHeinrich Schuchardt } else 258623b3a57SHeinrich Schuchardt #endif 259c7ae3dfdSSimon Glass { 260b9939336SAlexander Graf saddr = argv[1]; 261b9939336SAlexander Graf 262b9939336SAlexander Graf addr = simple_strtoul(saddr, NULL, 16); 263b9939336SAlexander Graf 2641c39809bSAlexander Graf if (argc > 2) { 2651c39809bSAlexander Graf sfdt = argv[2]; 2661c39809bSAlexander Graf fdt_addr = simple_strtoul(sfdt, NULL, 16); 2671c39809bSAlexander Graf } 268c7ae3dfdSSimon Glass } 2691c39809bSAlexander Graf 2705ee31bafSSimon Glass printf("## Starting EFI application at %08lx ...\n", addr); 27195c5553eSRob Clark r = do_bootefi_exec((void *)addr, (void *)fdt_addr, 27295c5553eSRob Clark bootefi_device_path, bootefi_image_path); 2731da1bac4Sxypron.glpk@gmx.de printf("## Application terminated, r = %lu\n", 2741da1bac4Sxypron.glpk@gmx.de r & ~EFI_ERROR_MASK); 275b9939336SAlexander Graf 2761da1bac4Sxypron.glpk@gmx.de if (r != EFI_SUCCESS) 2771da1bac4Sxypron.glpk@gmx.de return 1; 2781da1bac4Sxypron.glpk@gmx.de else 2791da1bac4Sxypron.glpk@gmx.de return 0; 280b9939336SAlexander Graf } 281b9939336SAlexander Graf 282b9939336SAlexander Graf #ifdef CONFIG_SYS_LONGHELP 283b9939336SAlexander Graf static char bootefi_help_text[] = 2841c39809bSAlexander Graf "<image address> [fdt address]\n" 2851c39809bSAlexander Graf " - boot EFI payload stored at address <image address>.\n" 2861c39809bSAlexander Graf " If specified, the device tree located at <fdt address> gets\n" 287c7ae3dfdSSimon Glass " exposed as EFI configuration table.\n" 288c7ae3dfdSSimon Glass #ifdef CONFIG_CMD_BOOTEFI_HELLO 289623b3a57SHeinrich Schuchardt "bootefi hello\n" 290623b3a57SHeinrich Schuchardt " - boot a sample Hello World application stored within U-Boot\n" 291623b3a57SHeinrich Schuchardt #endif 292623b3a57SHeinrich Schuchardt #ifdef CONFIG_CMD_BOOTEFI_SELFTEST 293623b3a57SHeinrich Schuchardt "bootefi selftest\n" 294623b3a57SHeinrich Schuchardt " - boot an EFI selftest application stored within U-Boot\n" 295c7ae3dfdSSimon Glass #endif 296c7ae3dfdSSimon Glass ; 297b9939336SAlexander Graf #endif 298b9939336SAlexander Graf 299b9939336SAlexander Graf U_BOOT_CMD( 3001c39809bSAlexander Graf bootefi, 3, 0, do_bootefi, 30192dfd922SSergey Kubushyn "Boots an EFI payload from memory", 302b9939336SAlexander Graf bootefi_help_text 303b9939336SAlexander Graf ); 3040f4060ebSAlexander Graf 30595c5553eSRob Clark static int parse_partnum(const char *devnr) 30695c5553eSRob Clark { 30795c5553eSRob Clark const char *str = strchr(devnr, ':'); 30895c5553eSRob Clark if (str) { 30995c5553eSRob Clark str++; 31095c5553eSRob Clark return simple_strtoul(str, NULL, 16); 31195c5553eSRob Clark } 31295c5553eSRob Clark return 0; 31395c5553eSRob Clark } 31495c5553eSRob Clark 315c07ad7c0SAlexander Graf void efi_set_bootdev(const char *dev, const char *devnr, const char *path) 3160f4060ebSAlexander Graf { 31795c5553eSRob Clark char filename[32] = { 0 }; /* dp->str is u16[32] long */ 31895c5553eSRob Clark char *s; 3190f4060ebSAlexander Graf 32095c5553eSRob Clark if (strcmp(dev, "Net")) { 32195c5553eSRob Clark struct blk_desc *desc; 32295c5553eSRob Clark int part; 32395c5553eSRob Clark 324f9d334bdSAlexander Graf desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10)); 32595c5553eSRob Clark part = parse_partnum(devnr); 326f9d334bdSAlexander Graf 32795c5553eSRob Clark bootefi_device_path = efi_dp_from_part(desc, part); 32895c5553eSRob Clark } else { 32995c5553eSRob Clark #ifdef CONFIG_NET 33095c5553eSRob Clark bootefi_device_path = efi_dp_from_eth(); 331f9d334bdSAlexander Graf #endif 332f9d334bdSAlexander Graf } 333f9d334bdSAlexander Graf 33449271666SAlexander Graf if (strcmp(dev, "Net")) { 33549271666SAlexander Graf /* Add leading / to fs paths, because they're absolute */ 33695c5553eSRob Clark snprintf(filename, sizeof(filename), "/%s", path); 33749271666SAlexander Graf } else { 33895c5553eSRob Clark snprintf(filename, sizeof(filename), "%s", path); 33949271666SAlexander Graf } 3403e433e96SRob Clark /* DOS style file path: */ 34195c5553eSRob Clark s = filename; 3423e433e96SRob Clark while ((s = strchr(s, '/'))) 3433e433e96SRob Clark *s++ = '\\'; 34495c5553eSRob Clark bootefi_image_path = efi_dp_from_file(NULL, 0, filename); 3450f4060ebSAlexander Graf } 346