bootefi.c 12.5 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 *  EFI application loader
 *
 *  Copyright (c) 2016 Alexander Graf
 *
 *  SPDX-License-Identifier:     GPL-2.0+
 */

9
#include <charset.h>
10 11
#include <common.h>
#include <command.h>
12
#include <dm.h>
13
#include <efi_loader.h>
14
#include <efi_selftest.h>
15 16 17
#include <errno.h>
#include <libfdt.h>
#include <libfdt_env.h>
18
#include <memalign.h>
19
#include <asm/global_data.h>
S
Simon Glass 已提交
20 21
#include <asm-generic/sections.h>
#include <linux/linkage.h>
22 23

DECLARE_GLOBAL_DATA_PTR;
24

25 26
static uint8_t efi_obj_list_initalized;

27 28
static struct efi_device_path *bootefi_image_path;
static struct efi_device_path *bootefi_device_path;
29

30 31 32 33 34
/* Initialize and populate EFI object list */
static void efi_init_obj_list(void)
{
	efi_obj_list_initalized = 1;

35 36 37
	/* Initialize EFI driver uclass */
	efi_driver_init();

38 39 40 41 42 43 44 45
	efi_console_register();
#ifdef CONFIG_PARTITIONS
	efi_disk_register();
#endif
#if defined(CONFIG_LCD) || defined(CONFIG_DM_VIDEO)
	efi_gop_register();
#endif
#ifdef CONFIG_NET
46
	efi_net_register();
47 48 49 50
#endif
#ifdef CONFIG_GENERATE_SMBIOS_TABLE
	efi_smbios_register();
#endif
51
	efi_watchdog_register();
52 53 54 55 56 57

	/* Initialize EFI runtime services */
	efi_reset_system_init();
	efi_get_time_init();
}

58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
/*
 * Set the load options of an image from an environment variable.
 *
 * @loaded_image_info:	the image
 * @env_var:		name of the environment variable
 */
static void set_load_options(struct efi_loaded_image *loaded_image_info,
			     const char *env_var)
{
	size_t size;
	const char *env = env_get(env_var);

	loaded_image_info->load_options = NULL;
	loaded_image_info->load_options_size = 0;
	if (!env)
		return;
	size = strlen(env) + 1;
	loaded_image_info->load_options = calloc(size, sizeof(u16));
	if (!loaded_image_info->load_options) {
		printf("ERROR: Out of memory\n");
		return;
	}
	utf8_to_utf16(loaded_image_info->load_options, (u8 *)env, size);
	loaded_image_info->load_options_size = size * 2;
}

84 85 86
static void *copy_fdt(void *fdt)
{
	u64 fdt_size = fdt_totalsize(fdt);
87 88
	unsigned long fdt_ram_start = -1L, fdt_pages;
	u64 new_fdt_addr;
89
	void *new_fdt;
90
	int i;
91

92 93 94
        for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
                u64 ram_start = gd->bd->bi_dram[i].start;
                u64 ram_size = gd->bd->bi_dram[i].size;
95

96 97 98 99 100 101 102 103
		if (!ram_size)
			continue;

		if (ram_start < fdt_ram_start)
			fdt_ram_start = ram_start;
	}

	/* Give us at least 4kb breathing room */
104
	fdt_size = ALIGN(fdt_size + 4096, EFI_PAGE_SIZE);
105 106 107 108
	fdt_pages = fdt_size >> EFI_PAGE_SHIFT;

	/* Safe fdt location is at 128MB */
	new_fdt_addr = fdt_ram_start + (128 * 1024 * 1024) + fdt_size;
109
	if (efi_allocate_pages(1, EFI_RUNTIME_SERVICES_DATA, fdt_pages,
110 111
			       &new_fdt_addr) != EFI_SUCCESS) {
		/* If we can't put it there, put it somewhere */
112
		new_fdt_addr = (ulong)memalign(EFI_PAGE_SIZE, fdt_size);
113
		if (efi_allocate_pages(1, EFI_RUNTIME_SERVICES_DATA, fdt_pages,
114 115 116 117
				       &new_fdt_addr) != EFI_SUCCESS) {
			printf("ERROR: Failed to reserve space for FDT\n");
			return NULL;
		}
118
	}
119

120
	new_fdt = (void*)(ulong)new_fdt_addr;
121 122 123 124 125 126
	memcpy(new_fdt, fdt, fdt_totalsize(fdt));
	fdt_set_totalsize(new_fdt, fdt_size);

	return new_fdt;
}

127
static efi_status_t efi_do_enter(
128
			efi_handle_t image_handle, struct efi_system_table *st,
129 130 131
			EFIAPI efi_status_t (*entry)(
				efi_handle_t image_handle,
				struct efi_system_table *st))
132 133 134 135 136 137 138 139 140
{
	efi_status_t ret = EFI_LOAD_ERROR;

	if (entry)
		ret = entry(image_handle, st);
	st->boottime->exit(image_handle, ret, 0, NULL);
	return ret;
}

141
#ifdef CONFIG_ARM64
142
static efi_status_t efi_run_in_el2(EFIAPI efi_status_t (*entry)(
143 144
			efi_handle_t image_handle, struct efi_system_table *st),
			efi_handle_t image_handle, struct efi_system_table *st)
145 146 147 148
{
	/* Enable caches again */
	dcache_enable();

149
	return efi_do_enter(image_handle, st, entry);
150 151 152
}
#endif

153 154 155 156
/*
 * Load an EFI payload into a newly allocated piece of memory, register all
 * EFI objects it would want to access and jump to it.
 */
157 158 159
static efi_status_t do_bootefi_exec(void *efi, void *fdt,
				    struct efi_device_path *device_path,
				    struct efi_device_path *image_path)
160
{
161 162
	struct efi_loaded_image loaded_image_info = {};
	struct efi_object loaded_image_info_obj = {};
163
	struct efi_device_path *memdp = NULL;
164 165
	ulong ret;

166 167
	EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
				     struct efi_system_table *st);
168
	ulong fdt_pages, fdt_size, fdt_start, fdt_end;
169
	const efi_guid_t fdt_guid = EFI_FDT_GUID;
170
	bootm_headers_t img = { 0 };
171

172 173 174 175 176 177 178 179 180 181 182 183 184 185
	/*
	 * Special case for efi payload not loaded from disk, such as
	 * 'bootefi hello' or for example payload loaded directly into
	 * memory via jtag/etc:
	 */
	if (!device_path && !image_path) {
		printf("WARNING: using memory device/image path, this may confuse some payloads!\n");
		/* actual addresses filled in after efi_load_pe() */
		memdp = efi_dp_from_mem(0, 0, 0);
		device_path = image_path = memdp;
	} else {
		assert(device_path && image_path);
	}

186 187 188 189 190 191 192
	/* Initialize and populate EFI object list */
	if (!efi_obj_list_initalized)
		efi_init_obj_list();

	efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj,
			       device_path, image_path);

193 194 195 196 197 198
	/*
	 * gd lives in a fixed register which may get clobbered while we execute
	 * the payload. So save it here and restore it on every callback entry
	 */
	efi_save_gd();

199
	if (fdt && !fdt_check_header(fdt)) {
200
		/* Prepare fdt for payload */
201 202 203
		fdt = copy_fdt(fdt);

		if (image_setup_libfdt(&img, fdt, 0, NULL)) {
204 205 206 207 208
			printf("ERROR: Failed to process device tree\n");
			return -EINVAL;
		}

		/* Link to it in the efi tables */
209
		efi_install_configuration_table(&fdt_guid, fdt);
210 211

		/* And reserve the space in the memory map */
212 213
		fdt_start = ((ulong)fdt) & ~EFI_PAGE_MASK;
		fdt_end = ((ulong)fdt) + fdt_totalsize(fdt);
214 215 216 217 218 219 220
		fdt_size = (fdt_end - fdt_start) + EFI_PAGE_MASK;
		fdt_pages = fdt_size >> EFI_PAGE_SHIFT;
		/* Give a bootloader the chance to modify the device tree */
		fdt_pages += 2;
		efi_add_memory_map(fdt_start, fdt_pages,
				   EFI_BOOT_SERVICES_DATA, true);
	} else {
221
		printf("WARNING: Invalid device tree, expect boot to fail\n");
222
		efi_install_configuration_table(&fdt_guid, NULL);
223 224
	}

225 226
	/* Transfer environment variable bootargs as load options */
	set_load_options(&loaded_image_info, "bootargs");
227 228
	/* Load the EFI payload */
	entry = efi_load_pe(efi, &loaded_image_info);
229 230 231 232
	if (!entry) {
		ret = -ENOENT;
		goto exit;
	}
233

234 235 236 237 238 239 240 241
	if (memdp) {
		struct efi_device_path_memory *mdp = (void *)memdp;
		mdp->memory_type = loaded_image_info.image_code_type;
		mdp->start_address = (uintptr_t)loaded_image_info.image_base;
		mdp->end_address = mdp->start_address +
				loaded_image_info.image_size;
	}

R
Rob Clark 已提交
242 243 244 245
	/* we don't support much: */
	env_set("efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported",
		"{ro,boot}(blob)0000000000000000");

246
	/* Call our payload! */
247
	debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry);
A
Alexander Graf 已提交
248 249

	if (setjmp(&loaded_image_info.exit_jmp)) {
250 251
		ret = loaded_image_info.exit_status;
		goto exit;
A
Alexander Graf 已提交
252 253
	}

254 255 256 257 258
#ifdef CONFIG_ARM64
	/* On AArch64 we need to make sure we call our payload in < EL3 */
	if (current_el() == 3) {
		smp_kick_all_cpus();
		dcache_disable();	/* flush cache before switch to EL2 */
259 260

		/* Move into EL2 and keep running there */
261 262
		armv8_switch_to_el2((ulong)entry,
				    (ulong)&loaded_image_info_obj.handle,
263
				    (ulong)&systab, 0, (ulong)efi_run_in_el2,
264 265 266 267
				    ES_TO_AARCH64);

		/* Should never reach here, efi exits with longjmp */
		while (1) { }
268 269 270
	}
#endif

271
	ret = efi_do_enter(loaded_image_info_obj.handle, &systab, entry);
272 273 274 275 276 277

exit:
	/* image has returned, loaded-image obj goes *poof*: */
	list_del(&loaded_image_info_obj.link);

	return ret;
278 279
}

R
Rob Clark 已提交
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
static int do_bootefi_bootmgr_exec(unsigned long fdt_addr)
{
	struct efi_device_path *device_path, *file_path;
	void *addr;
	efi_status_t r;

	/* Initialize and populate EFI object list */
	if (!efi_obj_list_initalized)
		efi_init_obj_list();

	/*
	 * gd lives in a fixed register which may get clobbered while we execute
	 * the payload. So save it here and restore it on every callback entry
	 */
	efi_save_gd();

	addr = efi_bootmgr_load(&device_path, &file_path);
	if (!addr)
		return 1;

	printf("## Starting EFI application at %p ...\n", addr);
	r = do_bootefi_exec(addr, (void *)fdt_addr, device_path, file_path);
	printf("## Application terminated, r = %lu\n",
	       r & ~EFI_ERROR_MASK);

	if (r != EFI_SUCCESS)
		return 1;

	return 0;
}

311 312 313
/* Interpreter command to boot an arbitrary EFI image from memory */
static int do_bootefi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
314 315
	char *saddr, *sfdt;
	unsigned long addr, fdt_addr = 0;
316
	efi_status_t r;
317 318

	if (argc < 2)
319
		return CMD_RET_USAGE;
320 321
#ifdef CONFIG_CMD_BOOTEFI_HELLO
	if (!strcmp(argv[1], "hello")) {
322
		ulong size = __efi_helloworld_end - __efi_helloworld_begin;
323

324 325 326 327 328
		saddr = env_get("loadaddr");
		if (saddr)
			addr = simple_strtoul(saddr, NULL, 16);
		else
			addr = CONFIG_SYS_LOAD_ADDR;
329
		memcpy((char *)addr, __efi_helloworld_begin, size);
330
	} else
331 332 333
#endif
#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
	if (!strcmp(argv[1], "selftest")) {
334 335 336
		struct efi_loaded_image loaded_image_info = {};
		struct efi_object loaded_image_info_obj = {};

337 338 339 340 341 342
		/* Construct a dummy device path. */
		bootefi_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE,
						      (uintptr_t)&efi_selftest,
						      (uintptr_t)&efi_selftest);
		bootefi_image_path = efi_dp_from_file(NULL, 0, "\\selftest");

343 344 345
		efi_setup_loaded_image(&loaded_image_info,
				       &loaded_image_info_obj,
				       bootefi_device_path, bootefi_image_path);
346 347 348 349 350 351 352 353 354
		/*
		 * gd lives in a fixed register which may get clobbered while we
		 * execute the payload. So save it here and restore it on every
		 * callback entry
		 */
		efi_save_gd();
		/* Initialize and populate EFI object list */
		if (!efi_obj_list_initalized)
			efi_init_obj_list();
355 356 357
		/* Transfer environment variable efi_selftest as load options */
		set_load_options(&loaded_image_info, "efi_selftest");
		/* Execute the test */
358
		r = efi_selftest(loaded_image_info_obj.handle, &systab);
359
		efi_restore_gd();
360
		free(loaded_image_info.load_options);
361 362
		list_del(&loaded_image_info_obj.link);
		return r != EFI_SUCCESS;
363
	} else
364
#endif
R
Rob Clark 已提交
365 366 367 368 369 370 371 372
	if (!strcmp(argv[1], "bootmgr")) {
		unsigned long fdt_addr = 0;

		if (argc > 2)
			fdt_addr = simple_strtoul(argv[2], NULL, 16);

		return do_bootefi_bootmgr_exec(fdt_addr);
	} else {
373
		saddr = argv[1];
374

375
		addr = simple_strtoul(saddr, NULL, 16);
376 377 378
		/* Check that a numeric value was passed */
		if (!addr && *saddr != '0')
			return CMD_RET_USAGE;
379 380 381 382 383

		if (argc > 2) {
			sfdt = argv[2];
			fdt_addr = simple_strtoul(sfdt, NULL, 16);
		}
384 385
	}

386
	printf("## Starting EFI application at %08lx ...\n", addr);
387 388
	r = do_bootefi_exec((void *)addr, (void *)fdt_addr,
			    bootefi_device_path, bootefi_image_path);
389 390
	printf("## Application terminated, r = %lu\n",
	       r & ~EFI_ERROR_MASK);
391

392 393 394 395
	if (r != EFI_SUCCESS)
		return 1;
	else
		return 0;
396 397 398 399
}

#ifdef CONFIG_SYS_LONGHELP
static char bootefi_help_text[] =
400 401 402
	"<image address> [fdt address]\n"
	"  - boot EFI payload stored at address <image address>.\n"
	"    If specified, the device tree located at <fdt address> gets\n"
403 404
	"    exposed as EFI configuration table.\n"
#ifdef CONFIG_CMD_BOOTEFI_HELLO
405 406 407 408 409 410
	"bootefi hello\n"
	"  - boot a sample Hello World application stored within U-Boot\n"
#endif
#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
	"bootefi selftest\n"
	"  - boot an EFI selftest application stored within U-Boot\n"
411 412
	"    Use environment variable efi_selftest to select a single test.\n"
	"    Use 'setenv efi_selftest list' to enumerate all tests.\n"
413
#endif
R
Rob Clark 已提交
414 415 416 417 418
	"bootmgr [fdt addr]\n"
	"  - load and boot EFI payload based on BootOrder/BootXXXX variables.\n"
	"\n"
	"    If specified, the device tree located at <fdt address> gets\n"
	"    exposed as EFI configuration table.\n";
419 420 421
#endif

U_BOOT_CMD(
422
	bootefi, 3, 0, do_bootefi,
S
Sergey Kubushyn 已提交
423
	"Boots an EFI payload from memory",
424 425
	bootefi_help_text
);
426

427
static int parse_partnum(const char *devnr)
428
{
429 430 431 432
	const char *str = strchr(devnr, ':');
	if (str) {
		str++;
		return simple_strtoul(str, NULL, 16);
433
	}
434 435
	return 0;
}
436

437 438 439 440
void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
{
	char filename[32] = { 0 }; /* dp->str is u16[32] long */
	char *s;
441

442 443 444
	if (strcmp(dev, "Net")) {
		struct blk_desc *desc;
		int part;
445

446
		desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10));
447 448
		if (!desc)
			return;
449
		part = parse_partnum(devnr);
450

451 452 453 454 455 456
		bootefi_device_path = efi_dp_from_part(desc, part);
	} else {
#ifdef CONFIG_NET
		bootefi_device_path = efi_dp_from_eth();
#endif
	}
457

R
Rob Clark 已提交
458 459 460
	if (!path)
		return;

461 462
	if (strcmp(dev, "Net")) {
		/* Add leading / to fs paths, because they're absolute */
463
		snprintf(filename, sizeof(filename), "/%s", path);
464
	} else {
465
		snprintf(filename, sizeof(filename), "%s", path);
466
	}
467
	/* DOS style file path: */
468
	s = filename;
469 470
	while ((s = strchr(s, '/')))
		*s++ = '\\';
471
	bootefi_image_path = efi_dp_from_file(NULL, 0, filename);
472
}