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

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

DECLARE_GLOBAL_DATA_PTR;
22

23 24
static uint8_t efi_obj_list_initalized;

25 26 27 28 29 30 31 32
/*
 * When booting using the "bootefi" command, we don't know which
 * physical device the file came from. So we create a pseudo-device
 * called "bootefi" with the device path /bootefi.
 *
 * In addition to the originating device we also declare the file path
 * of "bootefi" based loads to be /bootefi.
 */
33
static struct efi_device_path_file_path bootefi_image_path[] = {
34 35 36
	{
		.dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
		.dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
37
		.dp.length = sizeof(bootefi_image_path[0]),
38 39 40 41
		.str = { 'b','o','o','t','e','f','i' },
	}, {
		.dp.type = DEVICE_PATH_TYPE_END,
		.dp.sub_type = DEVICE_PATH_SUB_TYPE_END,
42
		.dp.length = sizeof(bootefi_image_path[0]),
43 44 45
	}
};

46 47 48 49 50 51 52 53 54 55 56 57 58
static struct efi_device_path_file_path bootefi_device_path[] = {
	{
		.dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
		.dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
		.dp.length = sizeof(bootefi_image_path[0]),
		.str = { 'b','o','o','t','e','f','i' },
	}, {
		.dp.type = DEVICE_PATH_TYPE_END,
		.dp.sub_type = DEVICE_PATH_SUB_TYPE_END,
		.dp.length = sizeof(bootefi_image_path[0]),
	}
};

59 60
/* The EFI loaded_image interface for the image executed via "bootefi" */
static struct efi_loaded_image loaded_image_info = {
61
	.device_handle = bootefi_device_path,
62
	.file_path = bootefi_image_path,
63 64 65 66 67 68 69 70 71 72 73 74
};

/* The EFI object struct for the image executed via "bootefi" */
static struct efi_object loaded_image_info_obj = {
	.handle = &loaded_image_info,
	.protocols = {
		{
			/*
			 * When asking for the loaded_image interface, just
			 * return handle which points to loaded_image_info
			 */
			.guid = &efi_guid_loaded_image,
75
			.protocol_interface = &loaded_image_info,
76 77 78 79
		},
		{
			/*
			 * When asking for the device path interface, return
80
			 * bootefi_device_path
81 82
			 */
			.guid = &efi_guid_device_path,
83
			.protocol_interface = bootefi_device_path,
84
		},
85 86 87 88
		{
			.guid = &efi_guid_console_control,
			.protocol_interface = (void *) &efi_console_control
		},
89 90 91 92
		{
			.guid = &efi_guid_device_path_to_text_protocol,
			.protocol_interface = (void *) &efi_device_path_to_text
		},
93 94 95 96 97
	},
};

/* The EFI object struct for the device the "bootefi" image was loaded from */
static struct efi_object bootefi_device_obj = {
98
	.handle = bootefi_device_path,
99 100 101
	.protocols = {
		{
			/* When asking for the device path interface, return
102
			 * bootefi_device_path */
103
			.guid = &efi_guid_device_path,
104
			.protocol_interface = bootefi_device_path
105 106 107 108
		}
	},
};

109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
/* Initialize and populate EFI object list */
static void efi_init_obj_list(void)
{
	efi_obj_list_initalized = 1;

	list_add_tail(&loaded_image_info_obj.link, &efi_obj_list);
	list_add_tail(&bootefi_device_obj.link, &efi_obj_list);
	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
	void *nethandle = loaded_image_info.device_handle;
	efi_net_register(&nethandle);

	if (!memcmp(bootefi_device_path[0].str, "N\0e\0t", 6))
		loaded_image_info.device_handle = nethandle;
	else
		loaded_image_info.device_handle = bootefi_device_path;
#endif
#ifdef CONFIG_GENERATE_SMBIOS_TABLE
	efi_smbios_register();
#endif

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

141 142 143
static void *copy_fdt(void *fdt)
{
	u64 fdt_size = fdt_totalsize(fdt);
144 145
	unsigned long fdt_ram_start = -1L, fdt_pages;
	u64 new_fdt_addr;
146
	void *new_fdt;
147
	int i;
148

149 150 151
        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;
152

153 154 155 156 157 158 159 160
		if (!ram_size)
			continue;

		if (ram_start < fdt_ram_start)
			fdt_ram_start = ram_start;
	}

	/* Give us at least 4kb breathing room */
161
	fdt_size = ALIGN(fdt_size + 4096, EFI_PAGE_SIZE);
162 163 164 165 166 167 168
	fdt_pages = fdt_size >> EFI_PAGE_SHIFT;

	/* Safe fdt location is at 128MB */
	new_fdt_addr = fdt_ram_start + (128 * 1024 * 1024) + fdt_size;
	if (efi_allocate_pages(1, EFI_BOOT_SERVICES_DATA, fdt_pages,
			       &new_fdt_addr) != EFI_SUCCESS) {
		/* If we can't put it there, put it somewhere */
169
		new_fdt_addr = (ulong)memalign(EFI_PAGE_SIZE, fdt_size);
170 171 172 173 174
		if (efi_allocate_pages(1, EFI_BOOT_SERVICES_DATA, fdt_pages,
				       &new_fdt_addr) != EFI_SUCCESS) {
			printf("ERROR: Failed to reserve space for FDT\n");
			return NULL;
		}
175
	}
176

177
	new_fdt = (void*)(ulong)new_fdt_addr;
178 179 180 181 182 183
	memcpy(new_fdt, fdt, fdt_totalsize(fdt));
	fdt_set_totalsize(new_fdt, fdt_size);

	return new_fdt;
}

184 185 186 187 188 189 190 191 192 193 194 195 196
static ulong efi_do_enter(void *image_handle,
			  struct efi_system_table *st,
			  asmlinkage ulong (*entry)(void *image_handle,
				struct efi_system_table *st))
{
	efi_status_t ret = EFI_LOAD_ERROR;

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

197
#ifdef CONFIG_ARM64
198 199 200
static unsigned long efi_run_in_el2(asmlinkage ulong (*entry)(
			void *image_handle, struct efi_system_table *st),
			void *image_handle, struct efi_system_table *st)
201 202 203 204
{
	/* Enable caches again */
	dcache_enable();

205
	return efi_do_enter(image_handle, st, entry);
206 207 208
}
#endif

209 210 211 212
/*
 * Load an EFI payload into a newly allocated piece of memory, register all
 * EFI objects it would want to access and jump to it.
 */
213
static unsigned long do_bootefi_exec(void *efi, void *fdt)
214
{
S
Simon Glass 已提交
215 216
	ulong (*entry)(void *image_handle, struct efi_system_table *st)
		asmlinkage;
217
	ulong fdt_pages, fdt_size, fdt_start, fdt_end;
218
	const efi_guid_t fdt_guid = EFI_FDT_GUID;
219
	bootm_headers_t img = { 0 };
220 221 222 223 224 225 226

	/*
	 * 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();

227
	if (fdt && !fdt_check_header(fdt)) {
228
		/* Prepare fdt for payload */
229 230 231
		fdt = copy_fdt(fdt);

		if (image_setup_libfdt(&img, fdt, 0, NULL)) {
232 233 234 235 236
			printf("ERROR: Failed to process device tree\n");
			return -EINVAL;
		}

		/* Link to it in the efi tables */
237
		efi_install_configuration_table(&fdt_guid, fdt);
238 239

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

	/* Load the EFI payload */
	entry = efi_load_pe(efi, &loaded_image_info);
	if (!entry)
		return -ENOENT;

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

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

	if (setjmp(&loaded_image_info.exit_jmp)) {
266
		return loaded_image_info.exit_status;
A
Alexander Graf 已提交
267 268
	}

269 270 271 272 273
#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 */
274 275 276

		/* Move into EL2 and keep running there */
		armv8_switch_to_el2((ulong)entry, (ulong)&loaded_image_info,
277
				    (ulong)&systab, 0, (ulong)efi_run_in_el2,
278 279 280 281
				    ES_TO_AARCH64);

		/* Should never reach here, efi exits with longjmp */
		while (1) { }
282 283 284
	}
#endif

285
	return efi_do_enter(&loaded_image_info, &systab, entry);
286 287 288 289 290
}

/* 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[])
{
291 292
	char *saddr, *sfdt;
	unsigned long addr, fdt_addr = 0;
293
	unsigned long r;
294 295

	if (argc < 2)
296
		return CMD_RET_USAGE;
297 298
#ifdef CONFIG_CMD_BOOTEFI_HELLO
	if (!strcmp(argv[1], "hello")) {
299
		ulong size = __efi_helloworld_end - __efi_helloworld_begin;
300

301 302 303 304 305
		saddr = env_get("loadaddr");
		if (saddr)
			addr = simple_strtoul(saddr, NULL, 16);
		else
			addr = CONFIG_SYS_LOAD_ADDR;
306
		memcpy((char *)addr, __efi_helloworld_begin, size);
307
	} else
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
#endif
#ifdef CONFIG_CMD_BOOTEFI_SELFTEST
	if (!strcmp(argv[1], "selftest")) {
		/*
		 * 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();
		loaded_image_info.device_handle = bootefi_device_path;
		loaded_image_info.file_path = bootefi_image_path;
		return efi_selftest(&loaded_image_info, &systab);
	} else
324 325 326
#endif
	{
		saddr = argv[1];
327

328 329 330 331 332 333
		addr = simple_strtoul(saddr, NULL, 16);

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

336
	printf("## Starting EFI application at %08lx ...\n", addr);
337
	r = do_bootefi_exec((void *)addr, (void*)fdt_addr);
338 339
	printf("## Application terminated, r = %lu\n",
	       r & ~EFI_ERROR_MASK);
340

341 342 343 344
	if (r != EFI_SUCCESS)
		return 1;
	else
		return 0;
345 346 347 348
}

#ifdef CONFIG_SYS_LONGHELP
static char bootefi_help_text[] =
349 350 351
	"<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"
352 353
	"    exposed as EFI configuration table.\n"
#ifdef CONFIG_CMD_BOOTEFI_HELLO
354 355 356 357 358 359
	"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"
360 361
#endif
	;
362 363 364
#endif

U_BOOT_CMD(
365
	bootefi, 3, 0, do_bootefi,
S
Sergey Kubushyn 已提交
366
	"Boots an EFI payload from memory",
367 368
	bootefi_help_text
);
369

370
void efi_set_bootdev(const char *dev, const char *devnr, const char *path)
371
{
372
	__maybe_unused struct blk_desc *desc;
373
	char devname[32] = { 0 }; /* dp->str is u16[32] long */
374
	char *colon, *s;
375

376
#if defined(CONFIG_BLK) || CONFIG_IS_ENABLED(ISO_PARTITION)
377 378 379 380 381 382 383 384 385 386 387 388 389 390
	desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10));
#endif

#ifdef CONFIG_BLK
	if (desc) {
		snprintf(devname, sizeof(devname), "%s", desc->bdev->name);
	} else
#endif

	{
		/* Assemble the condensed device name we use in efi_disk.c */
		snprintf(devname, sizeof(devname), "%s%s", dev, devnr);
	}

391
	colon = strchr(devname, ':');
392

393
#if CONFIG_IS_ENABLED(ISO_PARTITION)
394 395 396 397
	/* For ISOs we create partition block devices */
	if (desc && (desc->type != DEV_TYPE_UNKNOWN) &&
	    (desc->part_type == PART_TYPE_ISO)) {
		if (!colon)
398 399
			snprintf(devname, sizeof(devname), "%s:1", devname);

400 401 402 403
		colon = NULL;
	}
#endif

404 405 406
	if (colon)
		*colon = '\0';

407 408 409 410 411
	/* Patch bootefi_device_path to the target device */
	memset(bootefi_device_path[0].str, 0, sizeof(bootefi_device_path[0].str));
	ascii2unicode(bootefi_device_path[0].str, devname);

	/* Patch bootefi_image_path to the target file path */
412
	memset(bootefi_image_path[0].str, 0, sizeof(bootefi_image_path[0].str));
413 414 415 416 417 418
	if (strcmp(dev, "Net")) {
		/* Add leading / to fs paths, because they're absolute */
		snprintf(devname, sizeof(devname), "/%s", path);
	} else {
		snprintf(devname, sizeof(devname), "%s", path);
	}
419 420 421 422
	/* DOS style file path: */
	s = devname;
	while ((s = strchr(s, '/')))
		*s++ = '\\';
423 424
	ascii2unicode(bootefi_image_path[0].str, devname);
}