bootefi.c 13.2 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
#include <errno.h>
16 17
#include <linux/libfdt.h>
#include <linux/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 27
#define OBJ_LIST_NOT_INITIALIZED 1

static efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED;
28

29 30
static struct efi_device_path *bootefi_image_path;
static struct efi_device_path *bootefi_device_path;
31

32
/* Initialize and populate EFI object list */
33
efi_status_t efi_init_obj_list(void)
34
{
35 36
	efi_status_t ret = EFI_SUCCESS;

37
	/* Initialize once only */
38 39
	if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED)
		return efi_obj_list_initialized;
40

41
	/* Initialize EFI driver uclass */
42 43 44
	ret = efi_driver_init();
	if (ret != EFI_SUCCESS)
		goto out;
45

46 47 48
	ret = efi_console_register();
	if (ret != EFI_SUCCESS)
		goto out;
49
#ifdef CONFIG_PARTITIONS
50 51 52
	ret = efi_disk_register();
	if (ret != EFI_SUCCESS)
		goto out;
53 54
#endif
#if defined(CONFIG_LCD) || defined(CONFIG_DM_VIDEO)
55 56 57
	ret = efi_gop_register();
	if (ret != EFI_SUCCESS)
		goto out;
58
#endif
59
#ifdef CONFIG_CMD_NET
60 61 62
	ret = efi_net_register();
	if (ret != EFI_SUCCESS)
		goto out;
63 64
#endif
#ifdef CONFIG_GENERATE_SMBIOS_TABLE
65 66 67
	ret = efi_smbios_register();
	if (ret != EFI_SUCCESS)
		goto out;
68
#endif
69 70 71
	ret = efi_watchdog_register();
	if (ret != EFI_SUCCESS)
		goto out;
72 73

	/* Initialize EFI runtime services */
74 75 76 77 78 79 80 81 82 83
	ret = efi_reset_system_init();
	if (ret != EFI_SUCCESS)
		goto out;
	ret = efi_get_time_init();
	if (ret != EFI_SUCCESS)
		goto out;

out:
	efi_obj_list_initialized = ret;
	return ret;
84 85
}

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
/*
 * 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;
}

112 113 114
static void *copy_fdt(void *fdt)
{
	u64 fdt_size = fdt_totalsize(fdt);
115 116
	unsigned long fdt_ram_start = -1L, fdt_pages;
	u64 new_fdt_addr;
117
	void *new_fdt;
118
	int i;
119

120 121 122
        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;
123

124 125 126 127 128 129 130 131
		if (!ram_size)
			continue;

		if (ram_start < fdt_ram_start)
			fdt_ram_start = ram_start;
	}

	/* Give us at least 4kb breathing room */
132
	fdt_size = ALIGN(fdt_size + 4096, EFI_PAGE_SIZE);
133 134 135 136
	fdt_pages = fdt_size >> EFI_PAGE_SHIFT;

	/* Safe fdt location is at 128MB */
	new_fdt_addr = fdt_ram_start + (128 * 1024 * 1024) + fdt_size;
137
	if (efi_allocate_pages(1, EFI_RUNTIME_SERVICES_DATA, fdt_pages,
138 139
			       &new_fdt_addr) != EFI_SUCCESS) {
		/* If we can't put it there, put it somewhere */
140
		new_fdt_addr = (ulong)memalign(EFI_PAGE_SIZE, fdt_size);
141
		if (efi_allocate_pages(1, EFI_RUNTIME_SERVICES_DATA, fdt_pages,
142 143 144 145
				       &new_fdt_addr) != EFI_SUCCESS) {
			printf("ERROR: Failed to reserve space for FDT\n");
			return NULL;
		}
146
	}
147

148
	new_fdt = (void*)(ulong)new_fdt_addr;
149 150 151 152 153 154
	memcpy(new_fdt, fdt, fdt_totalsize(fdt));
	fdt_set_totalsize(new_fdt, fdt_size);

	return new_fdt;
}

155
static efi_status_t efi_do_enter(
156
			efi_handle_t image_handle, struct efi_system_table *st,
157 158 159
			EFIAPI efi_status_t (*entry)(
				efi_handle_t image_handle,
				struct efi_system_table *st))
160 161 162 163 164 165 166 167 168
{
	efi_status_t ret = EFI_LOAD_ERROR;

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

169
#ifdef CONFIG_ARM64
170
static efi_status_t efi_run_in_el2(EFIAPI efi_status_t (*entry)(
171 172
			efi_handle_t image_handle, struct efi_system_table *st),
			efi_handle_t image_handle, struct efi_system_table *st)
173 174 175 176
{
	/* Enable caches again */
	dcache_enable();

177
	return efi_do_enter(image_handle, st, entry);
178 179 180
}
#endif

181 182 183 184
/*
 * Load an EFI payload into a newly allocated piece of memory, register all
 * EFI objects it would want to access and jump to it.
 */
185 186 187
static efi_status_t do_bootefi_exec(void *efi, void *fdt,
				    struct efi_device_path *device_path,
				    struct efi_device_path *image_path)
188
{
189 190
	struct efi_loaded_image loaded_image_info = {};
	struct efi_object loaded_image_info_obj = {};
191
	struct efi_device_path *memdp = NULL;
192
	efi_status_t ret;
193

194 195
	EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
				     struct efi_system_table *st);
196
	ulong fdt_pages, fdt_size, fdt_start, fdt_end;
197
	const efi_guid_t fdt_guid = EFI_FDT_GUID;
198
	bootm_headers_t img = { 0 };
199

200 201 202 203 204 205 206 207 208 209 210 211 212 213
	/*
	 * 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);
	}

214 215 216
	efi_setup_loaded_image(&loaded_image_info, &loaded_image_info_obj,
			       device_path, image_path);

217 218 219 220 221 222
	/*
	 * 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();

223
	if (fdt && !fdt_check_header(fdt)) {
224
		/* Prepare fdt for payload */
225 226 227
		fdt = copy_fdt(fdt);

		if (image_setup_libfdt(&img, fdt, 0, NULL)) {
228 229 230 231 232
			printf("ERROR: Failed to process device tree\n");
			return -EINVAL;
		}

		/* Link to it in the efi tables */
233
		efi_install_configuration_table(&fdt_guid, fdt);
234 235

		/* And reserve the space in the memory map */
236 237
		fdt_start = ((ulong)fdt) & ~EFI_PAGE_MASK;
		fdt_end = ((ulong)fdt) + fdt_totalsize(fdt);
238 239 240 241 242 243 244
		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 {
245
		printf("WARNING: Invalid device tree, expect boot to fail\n");
246
		efi_install_configuration_table(&fdt_guid, NULL);
247 248
	}

249 250
	/* Transfer environment variable bootargs as load options */
	set_load_options(&loaded_image_info, "bootargs");
251 252
	/* Load the EFI payload */
	entry = efi_load_pe(efi, &loaded_image_info);
253
	if (!entry) {
254
		ret = EFI_LOAD_ERROR;
255 256
		goto exit;
	}
257

258 259 260 261 262 263 264 265
	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 已提交
266 267 268 269
	/* we don't support much: */
	env_set("efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported",
		"{ro,boot}(blob)0000000000000000");

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

	if (setjmp(&loaded_image_info.exit_jmp)) {
274 275
		ret = loaded_image_info.exit_status;
		goto exit;
A
Alexander Graf 已提交
276 277
	}

278 279 280 281 282
#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 */
283 284

		/* Move into EL2 and keep running there */
285 286
		armv8_switch_to_el2((ulong)entry,
				    (ulong)&loaded_image_info_obj.handle,
287
				    (ulong)&systab, 0, (ulong)efi_run_in_el2,
288 289 290 291
				    ES_TO_AARCH64);

		/* Should never reach here, efi exits with longjmp */
		while (1) { }
292 293 294
	}
#endif

295
	ret = efi_do_enter(loaded_image_info_obj.handle, &systab, entry);
296 297 298 299 300 301

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

	return ret;
302 303
}

R
Rob Clark 已提交
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
static int do_bootefi_bootmgr_exec(unsigned long fdt_addr)
{
	struct efi_device_path *device_path, *file_path;
	void *addr;
	efi_status_t r;

	/*
	 * 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;
}

331 332 333
/* 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[])
{
334 335
	char *saddr, *sfdt;
	unsigned long addr, fdt_addr = 0;
336
	efi_status_t r;
337

338 339 340 341 342 343 344 345
	/* Initialize EFI drivers */
	r = efi_init_obj_list();
	if (r != EFI_SUCCESS) {
		printf("Error: Cannot set up EFI drivers, r = %lu\n",
		       r & ~EFI_ERROR_MASK);
		return CMD_RET_FAILURE;
	}

346
	if (argc < 2)
347
		return CMD_RET_USAGE;
348 349
#ifdef CONFIG_CMD_BOOTEFI_HELLO
	if (!strcmp(argv[1], "hello")) {
350
		ulong size = __efi_helloworld_end - __efi_helloworld_begin;
351

352 353 354 355 356
		saddr = env_get("loadaddr");
		if (saddr)
			addr = simple_strtoul(saddr, NULL, 16);
		else
			addr = CONFIG_SYS_LOAD_ADDR;
357
		memcpy((char *)addr, __efi_helloworld_begin, size);
358
	} else
359 360 361
#endif
#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
	if (!strcmp(argv[1], "selftest")) {
362 363 364
		struct efi_loaded_image loaded_image_info = {};
		struct efi_object loaded_image_info_obj = {};

365 366 367 368 369 370
		/* 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");

371 372 373
		efi_setup_loaded_image(&loaded_image_info,
				       &loaded_image_info_obj,
				       bootefi_device_path, bootefi_image_path);
374 375 376 377 378 379 380
		/*
		 * 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 */
381
		efi_init_obj_list();
382 383 384
		/* Transfer environment variable efi_selftest as load options */
		set_load_options(&loaded_image_info, "efi_selftest");
		/* Execute the test */
385
		r = efi_selftest(loaded_image_info_obj.handle, &systab);
386
		efi_restore_gd();
387
		free(loaded_image_info.load_options);
388 389
		list_del(&loaded_image_info_obj.link);
		return r != EFI_SUCCESS;
390
	} else
391
#endif
R
Rob Clark 已提交
392 393 394 395 396 397 398 399
	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 {
400
		saddr = argv[1];
401

402
		addr = simple_strtoul(saddr, NULL, 16);
403 404 405
		/* Check that a numeric value was passed */
		if (!addr && *saddr != '0')
			return CMD_RET_USAGE;
406 407 408 409 410

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

413
	printf("## Starting EFI application at %08lx ...\n", addr);
414 415
	r = do_bootefi_exec((void *)addr, (void *)fdt_addr,
			    bootefi_device_path, bootefi_image_path);
416 417
	printf("## Application terminated, r = %lu\n",
	       r & ~EFI_ERROR_MASK);
418

419 420 421 422
	if (r != EFI_SUCCESS)
		return 1;
	else
		return 0;
423 424 425 426
}

#ifdef CONFIG_SYS_LONGHELP
static char bootefi_help_text[] =
427 428 429
	"<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"
430 431
	"    exposed as EFI configuration table.\n"
#ifdef CONFIG_CMD_BOOTEFI_HELLO
432 433 434 435 436 437
	"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"
438 439
	"    Use environment variable efi_selftest to select a single test.\n"
	"    Use 'setenv efi_selftest list' to enumerate all tests.\n"
440
#endif
441
	"bootefi bootmgr [fdt addr]\n"
R
Rob Clark 已提交
442 443 444 445
	"  - 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";
446 447 448
#endif

U_BOOT_CMD(
449
	bootefi, 3, 0, do_bootefi,
S
Sergey Kubushyn 已提交
450
	"Boots an EFI payload from memory",
451 452
	bootefi_help_text
);
453

454
static int parse_partnum(const char *devnr)
455
{
456 457 458 459
	const char *str = strchr(devnr, ':');
	if (str) {
		str++;
		return simple_strtoul(str, NULL, 16);
460
	}
461 462
	return 0;
}
463

464 465 466 467
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;
468

469 470 471
	if (strcmp(dev, "Net")) {
		struct blk_desc *desc;
		int part;
472

473
		desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10));
474 475
		if (!desc)
			return;
476
		part = parse_partnum(devnr);
477

478 479
		bootefi_device_path = efi_dp_from_part(desc, part);
	} else {
480
#ifdef CONFIG_CMD_NET
481 482 483
		bootefi_device_path = efi_dp_from_eth();
#endif
	}
484

R
Rob Clark 已提交
485 486 487
	if (!path)
		return;

488 489
	if (strcmp(dev, "Net")) {
		/* Add leading / to fs paths, because they're absolute */
490
		snprintf(filename, sizeof(filename), "/%s", path);
491
	} else {
492
		snprintf(filename, sizeof(filename), "%s", path);
493
	}
494
	/* DOS style file path: */
495
	s = filename;
496 497
	while ((s = strchr(s, '/')))
		*s++ = '\\';
498
	bootefi_image_path = efi_dp_from_file(NULL, 0, filename);
499
}