1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * EFI application loader 4 * 5 * Copyright (c) 2016 Alexander Graf 6 */ 7 8#define LOG_CATEGORY LOGC_EFI 9 10#include <command.h> 11#include <efi.h> 12#include <efi_loader.h> 13#include <exports.h> 14#include <log.h> 15#include <malloc.h> 16#include <mapmem.h> 17#include <vsprintf.h> 18#include <asm-generic/sections.h> 19#include <asm/global_data.h> 20#include <linux/string.h> 21 22DECLARE_GLOBAL_DATA_PTR; 23 24static struct efi_device_path *test_image_path; 25static struct efi_device_path *test_device_path; 26 27static efi_status_t bootefi_run_prepare(const char *load_options_path, 28 struct efi_device_path *device_path, 29 struct efi_device_path *image_path, 30 struct efi_loaded_image_obj **image_objp, 31 struct efi_loaded_image **loaded_image_infop) 32{ 33 efi_status_t ret; 34 u16 *load_options; 35 36 ret = efi_setup_loaded_image(device_path, image_path, image_objp, 37 loaded_image_infop); 38 if (ret != EFI_SUCCESS) 39 return ret; 40 41 /* Transfer environment variable as load options */ 42 return efi_env_set_load_options((efi_handle_t)*image_objp, 43 load_options_path, 44 &load_options); 45} 46 47/** 48 * bootefi_test_prepare() - prepare to run an EFI test 49 * 50 * Prepare to run a test as if it were provided by a loaded image. 51 * 52 * @image_objp: pointer to be set to the loaded image handle 53 * @loaded_image_infop: pointer to be set to the loaded image protocol 54 * @path: dummy file path used to construct the device path 55 * set in the loaded image protocol 56 * @load_options_path: name of a U-Boot environment variable. Its value is 57 * set as load options in the loaded image protocol. 58 * Return: status code 59 */ 60static efi_status_t bootefi_test_prepare 61 (struct efi_loaded_image_obj **image_objp, 62 struct efi_loaded_image **loaded_image_infop, const char *path, 63 const char *load_options_path) 64{ 65 efi_status_t ret; 66 67 /* Construct a dummy device path */ 68 test_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, 0, 0); 69 if (!test_device_path) 70 return EFI_OUT_OF_RESOURCES; 71 72 test_image_path = efi_dp_from_file(NULL, path); 73 if (!test_image_path) { 74 ret = EFI_OUT_OF_RESOURCES; 75 goto failure; 76 } 77 78 ret = bootefi_run_prepare(load_options_path, test_device_path, 79 test_image_path, image_objp, 80 loaded_image_infop); 81 if (ret == EFI_SUCCESS) 82 return ret; 83 84failure: 85 efi_free_pool(test_device_path); 86 efi_free_pool(test_image_path); 87 /* TODO: not sure calling clear function is necessary */ 88 efi_clear_bootdev(); 89 return ret; 90} 91 92/** 93 * do_efi_selftest() - execute EFI selftest 94 * 95 * Return: status code 96 */ 97static int do_efi_selftest(void) 98{ 99 struct efi_loaded_image_obj *image_obj; 100 struct efi_loaded_image *loaded_image_info; 101 efi_status_t ret; 102 103 ret = bootefi_test_prepare(&image_obj, &loaded_image_info, 104 "\\selftest", "efi_selftest"); 105 if (ret != EFI_SUCCESS) 106 return CMD_RET_FAILURE; 107 108 /* Execute the test */ 109 ret = EFI_CALL(efi_selftest(&image_obj->header, &systab)); 110 efi_restore_gd(); 111 free(loaded_image_info->load_options); 112 efi_free_pool(test_device_path); 113 efi_free_pool(test_image_path); 114 if (ret != EFI_SUCCESS) 115 efi_delete_handle(&image_obj->header); 116 else 117 ret = efi_delete_handle(&image_obj->header); 118 119 return ret != EFI_SUCCESS; 120} 121 122/** 123 * do_bootefi() - execute `bootefi` command 124 * 125 * @cmdtp: table entry describing command 126 * @flag: bitmap indicating how the command was invoked 127 * @argc: number of arguments 128 * @argv: command line arguments 129 * Return: status code 130 */ 131static int do_bootefi(struct cmd_tbl *cmdtp, int flag, int argc, 132 char *const argv[]) 133{ 134 efi_status_t ret; 135 char *p; 136 void *fdt, *image_buf; 137 unsigned long addr, size; 138 void *image_addr; 139 size_t image_size; 140 141 if (argc < 2) 142 return CMD_RET_USAGE; 143 144 if (argc > 2) { 145 uintptr_t fdt_addr; 146 147 fdt_addr = hextoul(argv[2], NULL); 148 fdt = map_sysmem(fdt_addr, 0); 149 } else { 150 fdt = EFI_FDT_USE_INTERNAL; 151 } 152 153 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && 154 !strcmp(argv[1], "bootmgr")) { 155 ret = efi_bootmgr_run(fdt); 156 157 if (ret != EFI_SUCCESS) 158 return CMD_RET_FAILURE; 159 160 return CMD_RET_SUCCESS; 161 } 162 163 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_SELFTEST) && 164 !strcmp(argv[1], "selftest")) { 165 /* Initialize EFI drivers */ 166 ret = efi_init_obj_list(); 167 if (ret != EFI_SUCCESS) { 168 log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n", 169 ret & ~EFI_ERROR_MASK); 170 return CMD_RET_FAILURE; 171 } 172 173 ret = efi_install_fdt(fdt); 174 if (ret != EFI_SUCCESS) 175 return CMD_RET_FAILURE; 176 177 return do_efi_selftest(); 178 } 179 180 if (!IS_ENABLED(CONFIG_CMD_BOOTEFI_BINARY)) 181 return CMD_RET_SUCCESS; 182 183 if (IS_ENABLED(CONFIG_CMD_BOOTEFI_HELLO) && 184 !strcmp(argv[1], "hello")) { 185 image_buf = __efi_helloworld_begin; 186 size = __efi_helloworld_end - __efi_helloworld_begin; 187 /* TODO: not sure calling clear function is necessary */ 188 efi_clear_bootdev(); 189 } else { 190 addr = strtoul(argv[1], NULL, 16); 191 /* Check that a numeric value was passed */ 192 if (!addr) 193 return CMD_RET_USAGE; 194 image_buf = map_sysmem(addr, 0); 195 196 p = strchr(argv[1], ':'); 197 if (p) { 198 size = strtoul(++p, NULL, 16); 199 if (!size) 200 return CMD_RET_USAGE; 201 efi_clear_bootdev(); 202 } else { 203 /* Image should be already loaded */ 204 efi_get_image_parameters(&image_addr, &image_size); 205 206 if (image_buf != image_addr) { 207 log_err("No UEFI binary known at %s\n", 208 argv[1]); 209 return CMD_RET_FAILURE; 210 } 211 size = image_size; 212 } 213 } 214 215 ret = efi_binary_run(image_buf, size, fdt); 216 217 if (ret != EFI_SUCCESS) 218 return CMD_RET_FAILURE; 219 220 return CMD_RET_SUCCESS; 221} 222 223U_BOOT_LONGHELP(bootefi, 224 "<image address>[:<image size>] [<fdt address>]\n" 225 " - boot EFI payload\n" 226#ifdef CONFIG_CMD_BOOTEFI_HELLO 227 "bootefi hello\n" 228 " - boot a sample Hello World application stored within U-Boot\n" 229#endif 230#ifdef CONFIG_CMD_BOOTEFI_SELFTEST 231 "bootefi selftest [fdt address]\n" 232 " - boot an EFI selftest application stored within U-Boot\n" 233 " Use environment variable efi_selftest to select a single test.\n" 234 " Use 'setenv efi_selftest list' to enumerate all tests.\n" 235#endif 236#ifdef CONFIG_CMD_BOOTEFI_BOOTMGR 237 "bootefi bootmgr [fdt address]\n" 238 " - load and boot EFI payload based on BootOrder/BootXXXX variables.\n" 239 "\n" 240 " If specified, the device tree located at <fdt address> gets\n" 241 " exposed as EFI configuration table.\n" 242#endif 243 ); 244 245U_BOOT_CMD( 246 bootefi, 4, 0, do_bootefi, 247 "Boots an EFI payload from memory", 248 bootefi_help_text 249); 250