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> 11b9939336SAlexander Graf #include <efi_loader.h> 12b9939336SAlexander Graf #include <errno.h> 13b9939336SAlexander Graf #include <libfdt.h> 14b9939336SAlexander Graf #include <libfdt_env.h> 15b9939336SAlexander Graf 16b9939336SAlexander Graf /* 17b9939336SAlexander Graf * When booting using the "bootefi" command, we don't know which 18b9939336SAlexander Graf * physical device the file came from. So we create a pseudo-device 19b9939336SAlexander Graf * called "bootefi" with the device path /bootefi. 20b9939336SAlexander Graf * 21b9939336SAlexander Graf * In addition to the originating device we also declare the file path 22b9939336SAlexander Graf * of "bootefi" based loads to be /bootefi. 23b9939336SAlexander Graf */ 240f4060ebSAlexander Graf static struct efi_device_path_file_path bootefi_image_path[] = { 25b9939336SAlexander Graf { 26b9939336SAlexander Graf .dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE, 27b9939336SAlexander Graf .dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH, 280f4060ebSAlexander Graf .dp.length = sizeof(bootefi_image_path[0]), 29b9939336SAlexander Graf .str = { 'b','o','o','t','e','f','i' }, 30b9939336SAlexander Graf }, { 31b9939336SAlexander Graf .dp.type = DEVICE_PATH_TYPE_END, 32b9939336SAlexander Graf .dp.sub_type = DEVICE_PATH_SUB_TYPE_END, 330f4060ebSAlexander Graf .dp.length = sizeof(bootefi_image_path[0]), 34b9939336SAlexander Graf } 35b9939336SAlexander Graf }; 36b9939336SAlexander Graf 37*c07ad7c0SAlexander Graf static struct efi_device_path_file_path bootefi_device_path[] = { 38*c07ad7c0SAlexander Graf { 39*c07ad7c0SAlexander Graf .dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE, 40*c07ad7c0SAlexander Graf .dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH, 41*c07ad7c0SAlexander Graf .dp.length = sizeof(bootefi_image_path[0]), 42*c07ad7c0SAlexander Graf .str = { 'b','o','o','t','e','f','i' }, 43*c07ad7c0SAlexander Graf }, { 44*c07ad7c0SAlexander Graf .dp.type = DEVICE_PATH_TYPE_END, 45*c07ad7c0SAlexander Graf .dp.sub_type = DEVICE_PATH_SUB_TYPE_END, 46*c07ad7c0SAlexander Graf .dp.length = sizeof(bootefi_image_path[0]), 47*c07ad7c0SAlexander Graf } 48*c07ad7c0SAlexander Graf }; 49*c07ad7c0SAlexander Graf 50b9939336SAlexander Graf static efi_status_t bootefi_open_dp(void *handle, efi_guid_t *protocol, 51b9939336SAlexander Graf void **protocol_interface, void *agent_handle, 52b9939336SAlexander Graf void *controller_handle, uint32_t attributes) 53b9939336SAlexander Graf { 54*c07ad7c0SAlexander Graf *protocol_interface = bootefi_device_path; 55b9939336SAlexander Graf return EFI_SUCCESS; 56b9939336SAlexander Graf } 57b9939336SAlexander Graf 58b9939336SAlexander Graf /* The EFI loaded_image interface for the image executed via "bootefi" */ 59b9939336SAlexander Graf static struct efi_loaded_image loaded_image_info = { 60*c07ad7c0SAlexander Graf .device_handle = bootefi_device_path, 610f4060ebSAlexander Graf .file_path = bootefi_image_path, 62b9939336SAlexander Graf }; 63b9939336SAlexander Graf 64b9939336SAlexander Graf /* The EFI object struct for the image executed via "bootefi" */ 65b9939336SAlexander Graf static struct efi_object loaded_image_info_obj = { 66b9939336SAlexander Graf .handle = &loaded_image_info, 67b9939336SAlexander Graf .protocols = { 68b9939336SAlexander Graf { 69b9939336SAlexander Graf /* 70b9939336SAlexander Graf * When asking for the loaded_image interface, just 71b9939336SAlexander Graf * return handle which points to loaded_image_info 72b9939336SAlexander Graf */ 73b9939336SAlexander Graf .guid = &efi_guid_loaded_image, 74b9939336SAlexander Graf .open = &efi_return_handle, 75b9939336SAlexander Graf }, 76b9939336SAlexander Graf { 77b9939336SAlexander Graf /* 78b9939336SAlexander Graf * When asking for the device path interface, return 79*c07ad7c0SAlexander Graf * bootefi_device_path 80b9939336SAlexander Graf */ 81b9939336SAlexander Graf .guid = &efi_guid_device_path, 82b9939336SAlexander Graf .open = &bootefi_open_dp, 83b9939336SAlexander Graf }, 84b9939336SAlexander Graf }, 85b9939336SAlexander Graf }; 86b9939336SAlexander Graf 87b9939336SAlexander Graf /* The EFI object struct for the device the "bootefi" image was loaded from */ 88b9939336SAlexander Graf static struct efi_object bootefi_device_obj = { 89*c07ad7c0SAlexander Graf .handle = bootefi_device_path, 90b9939336SAlexander Graf .protocols = { 91b9939336SAlexander Graf { 92b9939336SAlexander Graf /* When asking for the device path interface, return 93*c07ad7c0SAlexander Graf * bootefi_device_path */ 94b9939336SAlexander Graf .guid = &efi_guid_device_path, 95b9939336SAlexander Graf .open = &bootefi_open_dp, 96b9939336SAlexander Graf } 97b9939336SAlexander Graf }, 98b9939336SAlexander Graf }; 99b9939336SAlexander Graf 100b9939336SAlexander Graf /* 101b9939336SAlexander Graf * Load an EFI payload into a newly allocated piece of memory, register all 102b9939336SAlexander Graf * EFI objects it would want to access and jump to it. 103b9939336SAlexander Graf */ 104b9939336SAlexander Graf static unsigned long do_bootefi_exec(void *efi) 105b9939336SAlexander Graf { 106b9939336SAlexander Graf ulong (*entry)(void *image_handle, struct efi_system_table *st); 107b9939336SAlexander Graf ulong fdt_pages, fdt_size, fdt_start, fdt_end; 108dea2174dSAlexander Graf bootm_headers_t img = { 0 }; 109b9939336SAlexander Graf 110b9939336SAlexander Graf /* 111b9939336SAlexander Graf * gd lives in a fixed register which may get clobbered while we execute 112b9939336SAlexander Graf * the payload. So save it here and restore it on every callback entry 113b9939336SAlexander Graf */ 114b9939336SAlexander Graf efi_save_gd(); 115b9939336SAlexander Graf 116b9939336SAlexander Graf /* Update system table to point to our currently loaded FDT */ 117b9939336SAlexander Graf 118b9939336SAlexander Graf if (working_fdt) { 119dea2174dSAlexander Graf /* Prepare fdt for payload */ 120dea2174dSAlexander Graf if (image_setup_libfdt(&img, working_fdt, 0, NULL)) { 121dea2174dSAlexander Graf printf("ERROR: Failed to process device tree\n"); 122dea2174dSAlexander Graf return -EINVAL; 123dea2174dSAlexander Graf } 124dea2174dSAlexander Graf 125dea2174dSAlexander Graf /* Link to it in the efi tables */ 126b9939336SAlexander Graf systab.tables[0].guid = EFI_FDT_GUID; 127b9939336SAlexander Graf systab.tables[0].table = working_fdt; 128b9939336SAlexander Graf systab.nr_tables = 1; 129b9939336SAlexander Graf 130b9939336SAlexander Graf /* And reserve the space in the memory map */ 131b9939336SAlexander Graf fdt_start = ((ulong)working_fdt) & ~EFI_PAGE_MASK; 132b9939336SAlexander Graf fdt_end = ((ulong)working_fdt) + fdt_totalsize(working_fdt); 133b9939336SAlexander Graf fdt_size = (fdt_end - fdt_start) + EFI_PAGE_MASK; 134b9939336SAlexander Graf fdt_pages = fdt_size >> EFI_PAGE_SHIFT; 135b9939336SAlexander Graf /* Give a bootloader the chance to modify the device tree */ 136b9939336SAlexander Graf fdt_pages += 2; 137b9939336SAlexander Graf efi_add_memory_map(fdt_start, fdt_pages, 138b9939336SAlexander Graf EFI_BOOT_SERVICES_DATA, true); 139b9939336SAlexander Graf 140b9939336SAlexander Graf } else { 141b9939336SAlexander Graf printf("WARNING: No device tree loaded, expect boot to fail\n"); 142b9939336SAlexander Graf systab.nr_tables = 0; 143b9939336SAlexander Graf } 144b9939336SAlexander Graf 145b9939336SAlexander Graf /* Load the EFI payload */ 146b9939336SAlexander Graf entry = efi_load_pe(efi, &loaded_image_info); 147b9939336SAlexander Graf if (!entry) 148b9939336SAlexander Graf return -ENOENT; 149b9939336SAlexander Graf 150b9939336SAlexander Graf /* Initialize and populate EFI object list */ 151b9939336SAlexander Graf INIT_LIST_HEAD(&efi_obj_list); 152b9939336SAlexander Graf list_add_tail(&loaded_image_info_obj.link, &efi_obj_list); 153b9939336SAlexander Graf list_add_tail(&bootefi_device_obj.link, &efi_obj_list); 154b9939336SAlexander Graf #ifdef CONFIG_PARTITIONS 155b9939336SAlexander Graf efi_disk_register(); 156b9939336SAlexander Graf #endif 157be8d3241SAlexander Graf #ifdef CONFIG_LCD 158be8d3241SAlexander Graf efi_gop_register(); 159be8d3241SAlexander Graf #endif 160b9939336SAlexander Graf 161b9939336SAlexander Graf /* Call our payload! */ 162b9939336SAlexander Graf #ifdef DEBUG_EFI 163b9939336SAlexander Graf printf("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry); 164b9939336SAlexander Graf #endif 165b9939336SAlexander Graf return entry(&loaded_image_info, &systab); 166b9939336SAlexander Graf } 167b9939336SAlexander Graf 168b9939336SAlexander Graf 169b9939336SAlexander Graf /* Interpreter command to boot an arbitrary EFI image from memory */ 170b9939336SAlexander Graf static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 171b9939336SAlexander Graf { 172b9939336SAlexander Graf char *saddr; 173b9939336SAlexander Graf unsigned long addr; 174b9939336SAlexander Graf int r = 0; 175b9939336SAlexander Graf 176b9939336SAlexander Graf if (argc < 2) 177b9939336SAlexander Graf return 1; 178b9939336SAlexander Graf saddr = argv[1]; 179b9939336SAlexander Graf 180b9939336SAlexander Graf addr = simple_strtoul(saddr, NULL, 16); 181b9939336SAlexander Graf 182b9939336SAlexander Graf printf("## Starting EFI application at 0x%08lx ...\n", addr); 183b9939336SAlexander Graf r = do_bootefi_exec((void *)addr); 184b9939336SAlexander Graf printf("## Application terminated, r = %d\n", r); 185b9939336SAlexander Graf 186b9939336SAlexander Graf if (r != 0) 187b9939336SAlexander Graf r = 1; 188b9939336SAlexander Graf 189b9939336SAlexander Graf return r; 190b9939336SAlexander Graf } 191b9939336SAlexander Graf 192b9939336SAlexander Graf #ifdef CONFIG_SYS_LONGHELP 193b9939336SAlexander Graf static char bootefi_help_text[] = 194b9939336SAlexander Graf "<image address>\n" 195b9939336SAlexander Graf " - boot EFI payload stored at address <image address>\n" 196b9939336SAlexander Graf "\n" 197b9939336SAlexander Graf "Since most EFI payloads want to have a device tree provided, please\n" 198b9939336SAlexander Graf "make sure you load a device tree using the fdt addr command before\n" 199b9939336SAlexander Graf "executing bootefi.\n"; 200b9939336SAlexander Graf #endif 201b9939336SAlexander Graf 202b9939336SAlexander Graf U_BOOT_CMD( 203b9939336SAlexander Graf bootefi, 2, 0, do_bootefi, 204b9939336SAlexander Graf "Boots an EFI payload from memory\n", 205b9939336SAlexander Graf bootefi_help_text 206b9939336SAlexander Graf ); 2070f4060ebSAlexander Graf 208*c07ad7c0SAlexander Graf void efi_set_bootdev(const char *dev, const char *devnr, const char *path) 2090f4060ebSAlexander Graf { 2108c3df0bfSAlexander Graf __maybe_unused struct blk_desc *desc; 2110f4060ebSAlexander Graf char devname[16] = { 0 }; /* dp->str is u16[16] long */ 2120f4060ebSAlexander Graf char *colon; 2130f4060ebSAlexander Graf 2140f4060ebSAlexander Graf /* Assemble the condensed device name we use in efi_disk.c */ 2150f4060ebSAlexander Graf snprintf(devname, sizeof(devname), "%s%s", dev, devnr); 2160f4060ebSAlexander Graf colon = strchr(devname, ':'); 2178c3df0bfSAlexander Graf 2188c3df0bfSAlexander Graf #ifdef CONFIG_ISO_PARTITION 2198c3df0bfSAlexander Graf /* For ISOs we create partition block devices */ 2208c3df0bfSAlexander Graf desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10)); 2218c3df0bfSAlexander Graf if (desc && (desc->type != DEV_TYPE_UNKNOWN) && 2228c3df0bfSAlexander Graf (desc->part_type == PART_TYPE_ISO)) { 2238c3df0bfSAlexander Graf if (!colon) 2248c3df0bfSAlexander Graf snprintf(devname, sizeof(devname), "%s%s:1", dev, 2258c3df0bfSAlexander Graf devnr); 2268c3df0bfSAlexander Graf colon = NULL; 2278c3df0bfSAlexander Graf } 2288c3df0bfSAlexander Graf #endif 2298c3df0bfSAlexander Graf 2300f4060ebSAlexander Graf if (colon) 2310f4060ebSAlexander Graf *colon = '\0'; 2320f4060ebSAlexander Graf 233*c07ad7c0SAlexander Graf /* Patch bootefi_device_path to the target device */ 234*c07ad7c0SAlexander Graf memset(bootefi_device_path[0].str, 0, sizeof(bootefi_device_path[0].str)); 235*c07ad7c0SAlexander Graf ascii2unicode(bootefi_device_path[0].str, devname); 236*c07ad7c0SAlexander Graf 237*c07ad7c0SAlexander Graf /* Patch bootefi_image_path to the target file path */ 2380f4060ebSAlexander Graf memset(bootefi_image_path[0].str, 0, sizeof(bootefi_image_path[0].str)); 239*c07ad7c0SAlexander Graf snprintf(devname, sizeof(devname), "%s", path); 2400f4060ebSAlexander Graf ascii2unicode(bootefi_image_path[0].str, devname); 2410f4060ebSAlexander Graf } 242