X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=cmd%2Fbootefi.c;h=478bc116e242a167835d2033831d94034058931b;hb=eb0044db392247aa1c43e29f2703418d5668018a;hp=d66892e69ee963f893321878e961705996a0308f;hpb=9c7a0a600bfc8741e2941ce9bb965f2e77d6bbea;p=u-boot diff --git a/cmd/bootefi.c b/cmd/bootefi.c index d66892e69e..478bc116e2 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -8,98 +8,46 @@ #include #include +#include #include #include #include #include #include #include +#include +#include DECLARE_GLOBAL_DATA_PTR; -/* - * 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. - */ -static struct efi_device_path_file_path bootefi_image_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]), - } -}; - -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]), - } -}; +static uint8_t efi_obj_list_initalized; + +static struct efi_device_path *bootefi_image_path; +static struct efi_device_path *bootefi_device_path; -static efi_status_t bootefi_open_dp(void *handle, efi_guid_t *protocol, - void **protocol_interface, void *agent_handle, - void *controller_handle, uint32_t attributes) +/* Initialize and populate EFI object list */ +static void efi_init_obj_list(void) { - *protocol_interface = bootefi_device_path; - return EFI_SUCCESS; -} + efi_obj_list_initalized = 1; -/* The EFI loaded_image interface for the image executed via "bootefi" */ -static struct efi_loaded_image loaded_image_info = { - .device_handle = bootefi_device_path, - .file_path = bootefi_image_path, -}; - -/* 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, - .open = &efi_return_handle, - }, - { - /* - * When asking for the device path interface, return - * bootefi_device_path - */ - .guid = &efi_guid_device_path, - .open = &bootefi_open_dp, - }, - }, -}; - -/* The EFI object struct for the device the "bootefi" image was loaded from */ -static struct efi_object bootefi_device_obj = { - .handle = bootefi_device_path, - .protocols = { - { - /* When asking for the device path interface, return - * bootefi_device_path */ - .guid = &efi_guid_device_path, - .open = &bootefi_open_dp, - } - }, -}; + 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 + efi_net_register(); +#endif +#ifdef CONFIG_GENERATE_SMBIOS_TABLE + efi_smbios_register(); +#endif + + /* Initialize EFI runtime services */ + efi_reset_system_init(); + efi_get_time_init(); +} static void *copy_fdt(void *fdt) { @@ -121,7 +69,7 @@ static void *copy_fdt(void *fdt) } /* Give us at least 4kb breathing room */ - fdt_size = ALIGN(fdt_size + 4096, 4096); + fdt_size = ALIGN(fdt_size + 4096, EFI_PAGE_SIZE); fdt_pages = fdt_size >> EFI_PAGE_SHIFT; /* Safe fdt location is at 128MB */ @@ -129,8 +77,14 @@ static void *copy_fdt(void *fdt) 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 */ - new_fdt_addr = (ulong)memalign(4096, fdt_size); + new_fdt_addr = (ulong)memalign(EFI_PAGE_SIZE, fdt_size); + 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; + } } + new_fdt = (void*)(ulong)new_fdt_addr; memcpy(new_fdt, fdt, fdt_totalsize(fdt)); fdt_set_totalsize(new_fdt, fdt_size); @@ -138,16 +92,71 @@ static void *copy_fdt(void *fdt) return new_fdt; } +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; +} + +#ifdef CONFIG_ARM64 +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) +{ + /* Enable caches again */ + dcache_enable(); + + return efi_do_enter(image_handle, st, entry); +} +#endif + /* * Load an EFI payload into a newly allocated piece of memory, register all * EFI objects it would want to access and jump to it. */ -static unsigned long do_bootefi_exec(void *efi, void *fdt) +static unsigned long do_bootefi_exec(void *efi, void *fdt, + struct efi_device_path *device_path, + struct efi_device_path *image_path) { - ulong (*entry)(void *image_handle, struct efi_system_table *st); + struct efi_loaded_image loaded_image_info = {}; + struct efi_object loaded_image_info_obj = {}; + struct efi_device_path *memdp = NULL; + ulong ret; + + ulong (*entry)(void *image_handle, struct efi_system_table *st) + asmlinkage; ulong fdt_pages, fdt_size, fdt_start, fdt_end; + const efi_guid_t fdt_guid = EFI_FDT_GUID; bootm_headers_t img = { 0 }; + /* + * 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); + } + + /* 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); + /* * 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 @@ -164,9 +173,7 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt) } /* Link to it in the efi tables */ - systab.tables[0].guid = EFI_FDT_GUID; - systab.tables[0].table = fdt; - systab.nr_tables = 1; + efi_install_configuration_table(&fdt_guid, fdt); /* And reserve the space in the memory map */ fdt_start = ((ulong)fdt) & ~EFI_PAGE_MASK; @@ -179,70 +186,161 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt) EFI_BOOT_SERVICES_DATA, true); } else { printf("WARNING: Invalid device tree, expect boot to fail\n"); - systab.nr_tables = 0; + efi_install_configuration_table(&fdt_guid, NULL); } /* Load the EFI payload */ entry = efi_load_pe(efi, &loaded_image_info); - if (!entry) - return -ENOENT; + if (!entry) { + ret = -ENOENT; + goto exit; + } - /* Initialize and populate EFI object list */ - INIT_LIST_HEAD(&efi_obj_list); - list_add_tail(&loaded_image_info_obj.link, &efi_obj_list); - list_add_tail(&bootefi_device_obj.link, &efi_obj_list); -#ifdef CONFIG_PARTITIONS - efi_disk_register(); -#endif -#ifdef CONFIG_LCD - efi_gop_register(); -#endif -#ifdef CONFIG_NET - void *nethandle = loaded_image_info.device_handle; - efi_net_register(&nethandle); + 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; + } - if (!memcmp(bootefi_device_path[0].str, "N\0e\0t", 6)) - loaded_image_info.device_handle = nethandle; -#endif + /* we don't support much: */ + env_set("efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported", + "{ro,boot}(blob)0000000000000000"); /* Call our payload! */ debug("%s:%d Jumping to 0x%lx\n", __func__, __LINE__, (long)entry); if (setjmp(&loaded_image_info.exit_jmp)) { - efi_status_t status = loaded_image_info.exit_status; - return status == EFI_SUCCESS ? 0 : -EINVAL; + ret = loaded_image_info.exit_status; + goto exit; } - return entry(&loaded_image_info, &systab); +#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 */ + + /* Move into EL2 and keep running there */ + armv8_switch_to_el2((ulong)entry, (ulong)&loaded_image_info, + (ulong)&systab, 0, (ulong)efi_run_in_el2, + ES_TO_AARCH64); + + /* Should never reach here, efi exits with longjmp */ + while (1) { } + } +#endif + + ret = efi_do_enter(&loaded_image_info, &systab, entry); + +exit: + /* image has returned, loaded-image obj goes *poof*: */ + list_del(&loaded_image_info_obj.link); + + return ret; } +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; +} /* 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[]) { char *saddr, *sfdt; unsigned long addr, fdt_addr = 0; - int r = 0; + unsigned long r; if (argc < 2) - return 1; - saddr = argv[1]; + return CMD_RET_USAGE; +#ifdef CONFIG_CMD_BOOTEFI_HELLO + if (!strcmp(argv[1], "hello")) { + ulong size = __efi_helloworld_end - __efi_helloworld_begin; + + saddr = env_get("loadaddr"); + if (saddr) + addr = simple_strtoul(saddr, NULL, 16); + else + addr = CONFIG_SYS_LOAD_ADDR; + memcpy((char *)addr, __efi_helloworld_begin, size); + } else +#endif +#ifdef CONFIG_CMD_BOOTEFI_SELFTEST + if (!strcmp(argv[1], "selftest")) { + struct efi_loaded_image loaded_image_info = {}; + struct efi_object loaded_image_info_obj = {}; + + efi_setup_loaded_image(&loaded_image_info, + &loaded_image_info_obj, + bootefi_device_path, bootefi_image_path); + /* + * 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(); + return efi_selftest(&loaded_image_info, &systab); + } else +#endif + if (!strcmp(argv[1], "bootmgr")) { + unsigned long fdt_addr = 0; - addr = simple_strtoul(saddr, NULL, 16); + if (argc > 2) + fdt_addr = simple_strtoul(argv[2], NULL, 16); - if (argc > 2) { - sfdt = argv[2]; - fdt_addr = simple_strtoul(sfdt, NULL, 16); - } + return do_bootefi_bootmgr_exec(fdt_addr); + } else { + saddr = argv[1]; + + addr = simple_strtoul(saddr, NULL, 16); - printf("## Starting EFI application at 0x%08lx ...\n", addr); - r = do_bootefi_exec((void *)addr, (void*)fdt_addr); - printf("## Application terminated, r = %d\n", r); + if (argc > 2) { + sfdt = argv[2]; + fdt_addr = simple_strtoul(sfdt, NULL, 16); + } + } - if (r != 0) - r = 1; + printf("## Starting EFI application at %08lx ...\n", addr); + r = do_bootefi_exec((void *)addr, (void *)fdt_addr, + bootefi_device_path, bootefi_image_path); + printf("## Application terminated, r = %lu\n", + r & ~EFI_ERROR_MASK); - return r; + if (r != EFI_SUCCESS) + return 1; + else + return 0; } #ifdef CONFIG_SYS_LONGHELP @@ -250,6 +348,19 @@ static char bootefi_help_text[] = " [fdt address]\n" " - boot EFI payload stored at address .\n" " If specified, the device tree located at gets\n" + " exposed as EFI configuration table.\n" +#ifdef CONFIG_CMD_BOOTEFI_HELLO + "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" +#endif + "bootmgr [fdt addr]\n" + " - load and boot EFI payload based on BootOrder/BootXXXX variables.\n" + "\n" + " If specified, the device tree located at gets\n" " exposed as EFI configuration table.\n"; #endif @@ -259,42 +370,47 @@ U_BOOT_CMD( bootefi_help_text ); -void efi_set_bootdev(const char *dev, const char *devnr, const char *path) +static int parse_partnum(const char *devnr) { - __maybe_unused struct blk_desc *desc; - char devname[32] = { 0 }; /* dp->str is u16[32] long */ - char *colon; - - /* Assemble the condensed device name we use in efi_disk.c */ - snprintf(devname, sizeof(devname), "%s%s", dev, devnr); - colon = strchr(devname, ':'); - -#ifdef CONFIG_ISO_PARTITION - /* For ISOs we create partition block devices */ - desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10)); - if (desc && (desc->type != DEV_TYPE_UNKNOWN) && - (desc->part_type == PART_TYPE_ISO)) { - if (!colon) - snprintf(devname, sizeof(devname), "%s%s:1", dev, - devnr); - colon = NULL; + const char *str = strchr(devnr, ':'); + if (str) { + str++; + return simple_strtoul(str, NULL, 16); } -#endif + return 0; +} + +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; + + if (strcmp(dev, "Net")) { + struct blk_desc *desc; + int part; + + desc = blk_get_dev(dev, simple_strtol(devnr, NULL, 10)); + part = parse_partnum(devnr); - if (colon) - *colon = '\0'; + bootefi_device_path = efi_dp_from_part(desc, part); + } else { +#ifdef CONFIG_NET + bootefi_device_path = efi_dp_from_eth(); +#endif + } - /* 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); + if (!path) + return; - /* Patch bootefi_image_path to the target file path */ - memset(bootefi_image_path[0].str, 0, sizeof(bootefi_image_path[0].str)); if (strcmp(dev, "Net")) { /* Add leading / to fs paths, because they're absolute */ - snprintf(devname, sizeof(devname), "/%s", path); + snprintf(filename, sizeof(filename), "/%s", path); } else { - snprintf(devname, sizeof(devname), "%s", path); + snprintf(filename, sizeof(filename), "%s", path); } - ascii2unicode(bootefi_image_path[0].str, devname); + /* DOS style file path: */ + s = filename; + while ((s = strchr(s, '/'))) + *s++ = '\\'; + bootefi_image_path = efi_dp_from_file(NULL, 0, filename); }