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 */ 24*0f4060ebSAlexander 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, 28*0f4060ebSAlexander 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, 33*0f4060ebSAlexander Graf .dp.length = sizeof(bootefi_image_path[0]), 34b9939336SAlexander Graf } 35b9939336SAlexander Graf }; 36b9939336SAlexander Graf 37b9939336SAlexander Graf static efi_status_t bootefi_open_dp(void *handle, efi_guid_t *protocol, 38b9939336SAlexander Graf void **protocol_interface, void *agent_handle, 39b9939336SAlexander Graf void *controller_handle, uint32_t attributes) 40b9939336SAlexander Graf { 41*0f4060ebSAlexander Graf *protocol_interface = bootefi_image_path; 42b9939336SAlexander Graf return EFI_SUCCESS; 43b9939336SAlexander Graf } 44b9939336SAlexander Graf 45b9939336SAlexander Graf /* The EFI loaded_image interface for the image executed via "bootefi" */ 46b9939336SAlexander Graf static struct efi_loaded_image loaded_image_info = { 47*0f4060ebSAlexander Graf .device_handle = bootefi_image_path, 48*0f4060ebSAlexander Graf .file_path = bootefi_image_path, 49b9939336SAlexander Graf }; 50b9939336SAlexander Graf 51b9939336SAlexander Graf /* The EFI object struct for the image executed via "bootefi" */ 52b9939336SAlexander Graf static struct efi_object loaded_image_info_obj = { 53b9939336SAlexander Graf .handle = &loaded_image_info, 54b9939336SAlexander Graf .protocols = { 55b9939336SAlexander Graf { 56b9939336SAlexander Graf /* 57b9939336SAlexander Graf * When asking for the loaded_image interface, just 58b9939336SAlexander Graf * return handle which points to loaded_image_info 59b9939336SAlexander Graf */ 60b9939336SAlexander Graf .guid = &efi_guid_loaded_image, 61b9939336SAlexander Graf .open = &efi_return_handle, 62b9939336SAlexander Graf }, 63b9939336SAlexander Graf { 64b9939336SAlexander Graf /* 65b9939336SAlexander Graf * When asking for the device path interface, return 66*0f4060ebSAlexander Graf * bootefi_image_path 67b9939336SAlexander Graf */ 68b9939336SAlexander Graf .guid = &efi_guid_device_path, 69b9939336SAlexander Graf .open = &bootefi_open_dp, 70b9939336SAlexander Graf }, 71b9939336SAlexander Graf }, 72b9939336SAlexander Graf }; 73b9939336SAlexander Graf 74b9939336SAlexander Graf /* The EFI object struct for the device the "bootefi" image was loaded from */ 75b9939336SAlexander Graf static struct efi_object bootefi_device_obj = { 76*0f4060ebSAlexander Graf .handle = bootefi_image_path, 77b9939336SAlexander Graf .protocols = { 78b9939336SAlexander Graf { 79b9939336SAlexander Graf /* When asking for the device path interface, return 80*0f4060ebSAlexander Graf * bootefi_image_path */ 81b9939336SAlexander Graf .guid = &efi_guid_device_path, 82b9939336SAlexander Graf .open = &bootefi_open_dp, 83b9939336SAlexander Graf } 84b9939336SAlexander Graf }, 85b9939336SAlexander Graf }; 86b9939336SAlexander Graf 87b9939336SAlexander Graf /* 88b9939336SAlexander Graf * Load an EFI payload into a newly allocated piece of memory, register all 89b9939336SAlexander Graf * EFI objects it would want to access and jump to it. 90b9939336SAlexander Graf */ 91b9939336SAlexander Graf static unsigned long do_bootefi_exec(void *efi) 92b9939336SAlexander Graf { 93b9939336SAlexander Graf ulong (*entry)(void *image_handle, struct efi_system_table *st); 94b9939336SAlexander Graf ulong fdt_pages, fdt_size, fdt_start, fdt_end; 95dea2174dSAlexander Graf bootm_headers_t img = { 0 }; 96b9939336SAlexander Graf 97b9939336SAlexander Graf /* 98b9939336SAlexander Graf * gd lives in a fixed register which may get clobbered while we execute 99b9939336SAlexander Graf * the payload. So save it here and restore it on every callback entry 100b9939336SAlexander Graf */ 101b9939336SAlexander Graf efi_save_gd(); 102b9939336SAlexander Graf 103b9939336SAlexander Graf /* Update system table to point to our currently loaded FDT */ 104b9939336SAlexander Graf 105b9939336SAlexander Graf if (working_fdt) { 106dea2174dSAlexander Graf /* Prepare fdt for payload */ 107dea2174dSAlexander Graf if (image_setup_libfdt(&img, working_fdt, 0, NULL)) { 108dea2174dSAlexander Graf printf("ERROR: Failed to process device tree\n"); 109dea2174dSAlexander Graf return -EINVAL; 110dea2174dSAlexander Graf } 111dea2174dSAlexander Graf 112dea2174dSAlexander Graf /* Link to it in the efi tables */ 113b9939336SAlexander Graf systab.tables[0].guid = EFI_FDT_GUID; 114b9939336SAlexander Graf systab.tables[0].table = working_fdt; 115b9939336SAlexander Graf systab.nr_tables = 1; 116b9939336SAlexander Graf 117b9939336SAlexander Graf /* And reserve the space in the memory map */ 118b9939336SAlexander Graf fdt_start = ((ulong)working_fdt) & ~EFI_PAGE_MASK; 119b9939336SAlexander Graf fdt_end = ((ulong)working_fdt) + fdt_totalsize(working_fdt); 120b9939336SAlexander Graf fdt_size = (fdt_end - fdt_start) + EFI_PAGE_MASK; 121b9939336SAlexander Graf fdt_pages = fdt_size >> EFI_PAGE_SHIFT; 122b9939336SAlexander Graf /* Give a bootloader the chance to modify the device tree */ 123b9939336SAlexander Graf fdt_pages += 2; 124b9939336SAlexander Graf efi_add_memory_map(fdt_start, fdt_pages, 125b9939336SAlexander Graf EFI_BOOT_SERVICES_DATA, true); 126b9939336SAlexander Graf 127b9939336SAlexander Graf } else { 128b9939336SAlexander Graf printf("WARNING: No device tree loaded, expect boot to fail\n"); 129b9939336SAlexander Graf systab.nr_tables = 0; 130b9939336SAlexander Graf } 131b9939336SAlexander Graf 132b9939336SAlexander Graf /* Load the EFI payload */ 133b9939336SAlexander Graf entry = efi_load_pe(efi, &loaded_image_info); 134b9939336SAlexander Graf if (!entry) 135b9939336SAlexander Graf return -ENOENT; 136b9939336SAlexander Graf 137b9939336SAlexander Graf /* Initialize and populate EFI object list */ 138b9939336SAlexander Graf INIT_LIST_HEAD(&efi_obj_list); 139b9939336SAlexander Graf list_add_tail(&loaded_image_info_obj.link, &efi_obj_list); 140b9939336SAlexander Graf list_add_tail(&bootefi_device_obj.link, &efi_obj_list); 141b9939336SAlexander Graf #ifdef CONFIG_PARTITIONS 142b9939336SAlexander Graf efi_disk_register(); 143b9939336SAlexander Graf #endif 144b9939336SAlexander Graf 145b9939336SAlexander Graf /* Call our payload! */ 146b9939336SAlexander Graf #ifdef DEBUG_EFI 147b9939336SAlexander Graf printf("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry); 148b9939336SAlexander Graf #endif 149b9939336SAlexander Graf return entry(&loaded_image_info, &systab); 150b9939336SAlexander Graf } 151b9939336SAlexander Graf 152b9939336SAlexander Graf 153b9939336SAlexander Graf /* Interpreter command to boot an arbitrary EFI image from memory */ 154b9939336SAlexander Graf static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 155b9939336SAlexander Graf { 156b9939336SAlexander Graf char *saddr; 157b9939336SAlexander Graf unsigned long addr; 158b9939336SAlexander Graf int r = 0; 159b9939336SAlexander Graf 160b9939336SAlexander Graf if (argc < 2) 161b9939336SAlexander Graf return 1; 162b9939336SAlexander Graf saddr = argv[1]; 163b9939336SAlexander Graf 164b9939336SAlexander Graf addr = simple_strtoul(saddr, NULL, 16); 165b9939336SAlexander Graf 166b9939336SAlexander Graf printf("## Starting EFI application at 0x%08lx ...\n", addr); 167b9939336SAlexander Graf r = do_bootefi_exec((void *)addr); 168b9939336SAlexander Graf printf("## Application terminated, r = %d\n", r); 169b9939336SAlexander Graf 170b9939336SAlexander Graf if (r != 0) 171b9939336SAlexander Graf r = 1; 172b9939336SAlexander Graf 173b9939336SAlexander Graf return r; 174b9939336SAlexander Graf } 175b9939336SAlexander Graf 176b9939336SAlexander Graf #ifdef CONFIG_SYS_LONGHELP 177b9939336SAlexander Graf static char bootefi_help_text[] = 178b9939336SAlexander Graf "<image address>\n" 179b9939336SAlexander Graf " - boot EFI payload stored at address <image address>\n" 180b9939336SAlexander Graf "\n" 181b9939336SAlexander Graf "Since most EFI payloads want to have a device tree provided, please\n" 182b9939336SAlexander Graf "make sure you load a device tree using the fdt addr command before\n" 183b9939336SAlexander Graf "executing bootefi.\n"; 184b9939336SAlexander Graf #endif 185b9939336SAlexander Graf 186b9939336SAlexander Graf U_BOOT_CMD( 187b9939336SAlexander Graf bootefi, 2, 0, do_bootefi, 188b9939336SAlexander Graf "Boots an EFI payload from memory\n", 189b9939336SAlexander Graf bootefi_help_text 190b9939336SAlexander Graf ); 191*0f4060ebSAlexander Graf 192*0f4060ebSAlexander Graf void efi_set_bootdev(const char *dev, const char *devnr) 193*0f4060ebSAlexander Graf { 194*0f4060ebSAlexander Graf char devname[16] = { 0 }; /* dp->str is u16[16] long */ 195*0f4060ebSAlexander Graf char *colon; 196*0f4060ebSAlexander Graf 197*0f4060ebSAlexander Graf /* Assemble the condensed device name we use in efi_disk.c */ 198*0f4060ebSAlexander Graf snprintf(devname, sizeof(devname), "%s%s", dev, devnr); 199*0f4060ebSAlexander Graf colon = strchr(devname, ':'); 200*0f4060ebSAlexander Graf if (colon) 201*0f4060ebSAlexander Graf *colon = '\0'; 202*0f4060ebSAlexander Graf 203*0f4060ebSAlexander Graf /* Patch the bootefi_image_path to the target device */ 204*0f4060ebSAlexander Graf memset(bootefi_image_path[0].str, 0, sizeof(bootefi_image_path[0].str)); 205*0f4060ebSAlexander Graf ascii2unicode(bootefi_image_path[0].str, devname); 206*0f4060ebSAlexander Graf } 207