diff options
Diffstat (limited to 'lib/efi_loader')
-rw-r--r-- | lib/efi_loader/efi_bootmgr.c | 14 | ||||
-rw-r--r-- | lib/efi_loader/efi_boottime.c | 148 | ||||
-rw-r--r-- | lib/efi_loader/efi_device_path.c | 16 | ||||
-rw-r--r-- | lib/efi_loader/efi_file.c | 12 | ||||
-rw-r--r-- | lib/efi_loader/efi_image_loader.c | 67 |
5 files changed, 166 insertions, 91 deletions
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index a095df3f54..417016102b 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -14,10 +14,6 @@ static const struct efi_boot_services *bs; static const struct efi_runtime_services *rs; -#define LOAD_OPTION_ACTIVE 0x00000001 -#define LOAD_OPTION_FORCE_RECONNECT 0x00000002 -#define LOAD_OPTION_HIDDEN 0x00000008 - /* * bootmgr implements the logic of trying to find a payload to boot * based on the BootOrder + BootXXXX variables, and then loading it. @@ -150,7 +146,7 @@ static void *try_load_entry(uint16_t n, struct efi_device_path **device_path, debug("%s: trying to load \"%ls\" from %pD\n", __func__, lo.label, lo.file_path); - ret = efi_load_image_from_path(lo.file_path, &image); + ret = efi_load_image_from_path(lo.file_path, &image, &size); if (ret != EFI_SUCCESS) goto error; @@ -178,14 +174,14 @@ void *efi_bootmgr_load(struct efi_device_path **device_path, void *image = NULL; int i, num; - __efi_entry_check(); - bs = systab.boottime; rs = systab.runtime; bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size); - if (!bootorder) + if (!bootorder) { + printf("BootOrder not defined\n"); goto error; + } num = size / sizeof(uint16_t); for (i = 0; i < num; i++) { @@ -198,7 +194,5 @@ void *efi_bootmgr_load(struct efi_device_path **device_path, free(bootorder); error: - __efi_exit_check(); - return image; } diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index f74f989e0a..bd8b8a17ae 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -44,7 +44,8 @@ static bool efi_is_direct_boot = true; static volatile void *efi_gd, *app_gd; #endif -static int entry_count; +/* 1 if inside U-Boot code, 0 if inside EFI payload code */ +static int entry_count = 1; static int nesting_level; /* GUID of the device tree table */ const efi_guid_t efi_guid_fdt = EFI_FDT_GUID; @@ -1497,15 +1498,18 @@ static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid, /** * efi_setup_loaded_image() - initialize a loaded image - * @info: loaded image info to be passed to the entry point of the image - * @obj: internal object associated with the loaded image - * @device_path: device path of the loaded image - * @file_path: file path of the loaded image * * Initialize a loaded_image_info and loaded_image_info object with correct * protocols, boot-device, etc. * - * Return: status code + * In case of an error *handle_ptr and *info_ptr are set to NULL and an error + * code is returned. + * + * @device_path: device path of the loaded image + * @file_path: file path of the loaded image + * @handle_ptr: handle of the loaded image + * @info_ptr: loaded image protocol + * Return: status code */ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, struct efi_device_path *file_path, @@ -1513,8 +1517,12 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, struct efi_loaded_image **info_ptr) { efi_status_t ret; - struct efi_loaded_image *info; - struct efi_loaded_image_obj *obj; + struct efi_loaded_image *info = NULL; + struct efi_loaded_image_obj *obj = NULL; + + /* In case of EFI_OUT_OF_RESOURCES avoid illegal free by caller. */ + *handle_ptr = NULL; + *info_ptr = NULL; info = calloc(1, sizeof(*info)); if (!info) @@ -1528,11 +1536,6 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, /* Add internal object to object list */ efi_add_handle(&obj->header); - if (info_ptr) - *info_ptr = info; - if (handle_ptr) - *handle_ptr = obj; - info->revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION; info->file_path = file_path; info->system_table = &systab; @@ -1578,58 +1581,87 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, goto failure; #endif + if (info_ptr) + *info_ptr = info; + if (handle_ptr) + *handle_ptr = obj; + return ret; failure: printf("ERROR: Failure to install protocols for loaded image\n"); + efi_delete_handle(&obj->header); + free(info); return ret; } /** * efi_load_image_from_path() - load an image using a file path - * @file_path: the path of the image to load - * @buffer: buffer containing the loaded image * - * Return: status code + * Read a file into a buffer allocated as EFI_BOOT_SERVICES_DATA. It is the + * callers obligation to update the memory type as needed. + * + * @file_path: the path of the image to load + * @buffer: buffer containing the loaded image + * @size: size of the loaded image + * Return: status code */ efi_status_t efi_load_image_from_path(struct efi_device_path *file_path, - void **buffer) + void **buffer, efi_uintn_t *size) { struct efi_file_info *info = NULL; struct efi_file_handle *f; static efi_status_t ret; + u64 addr; efi_uintn_t bs; + /* In case of failure nothing is returned */ + *buffer = NULL; + *size = 0; + + /* Open file */ f = efi_file_from_path(file_path); if (!f) return EFI_DEVICE_ERROR; + /* Get file size */ bs = 0; EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs, info)); - if (ret == EFI_BUFFER_TOO_SMALL) { - info = malloc(bs); - EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, - &bs, info)); - } - if (ret != EFI_SUCCESS) + if (ret != EFI_BUFFER_TOO_SMALL) { + ret = EFI_DEVICE_ERROR; goto error; + } - ret = efi_allocate_pool(EFI_LOADER_DATA, info->file_size, buffer); - if (ret) + info = malloc(bs); + EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs, + info)); + if (ret != EFI_SUCCESS) goto error; + /* + * When reading the file we do not yet know if it contains an + * application, a boottime driver, or a runtime driver. So here we + * allocate a buffer as EFI_BOOT_SERVICES_DATA. The caller has to + * update the reservation according to the image type. + */ bs = info->file_size; - EFI_CALL(ret = f->read(f, &bs, *buffer)); - -error: - free(info); - EFI_CALL(f->close(f)); - + ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_BOOT_SERVICES_DATA, + efi_size_in_pages(bs), &addr); if (ret != EFI_SUCCESS) { - efi_free_pool(*buffer); - *buffer = NULL; + ret = EFI_OUT_OF_RESOURCES; + goto error; } + /* Read file */ + EFI_CALL(ret = f->read(f, &bs, (void *)(uintptr_t)addr)); + if (ret != EFI_SUCCESS) + efi_free_pages(addr, efi_size_in_pages(bs)); + *buffer = (void *)(uintptr_t)addr; + *size = bs; +error: + EFI_CALL(f->close(f)); + free(info); return ret; } @@ -1656,6 +1688,7 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy, efi_uintn_t source_size, efi_handle_t *image_handle) { + struct efi_device_path *dp, *fp; struct efi_loaded_image *info = NULL; struct efi_loaded_image_obj **image_obj = (struct efi_loaded_image_obj **)image_handle; @@ -1675,36 +1708,51 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy, } if (!source_buffer) { - struct efi_device_path *dp, *fp; - - ret = efi_load_image_from_path(file_path, &source_buffer); + ret = efi_load_image_from_path(file_path, &source_buffer, + &source_size); if (ret != EFI_SUCCESS) - goto failure; + goto error; /* * split file_path which contains both the device and * file parts: */ efi_dp_split_file_path(file_path, &dp, &fp); - ret = efi_setup_loaded_image(dp, fp, image_obj, &info); - if (ret != EFI_SUCCESS) - goto failure; } else { /* In this case, file_path is the "device" path, i.e. * something like a HARDWARE_DEVICE:MEMORY_MAPPED */ - ret = efi_setup_loaded_image(file_path, NULL, image_obj, &info); + u64 addr; + void *dest_buffer; + + ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_CODE, + efi_size_in_pages(source_size), &addr); if (ret != EFI_SUCCESS) goto error; + dest_buffer = (void *)(uintptr_t)addr; + memcpy(dest_buffer, source_buffer, source_size); + source_buffer = dest_buffer; + + dp = file_path; + fp = NULL; } - (*image_obj)->entry = efi_load_pe(*image_obj, source_buffer, info); - if (!(*image_obj)->entry) { - ret = EFI_UNSUPPORTED; - goto failure; - } + ret = efi_setup_loaded_image(dp, fp, image_obj, &info); + if (ret != EFI_SUCCESS) + goto error_invalid_image; + ret = efi_load_pe(*image_obj, source_buffer, info); + if (ret != EFI_SUCCESS) + goto error_invalid_image; + /* Update the type of the allocated memory */ + efi_add_memory_map((uintptr_t)source_buffer, + efi_size_in_pages(source_size), + info->image_code_type, false); info->system_table = &systab; info->parent_handle = parent_image; return EFI_EXIT(EFI_SUCCESS); -failure: +error_invalid_image: + /* The image is invalid. Release all associated resources. */ + efi_free_pages((uintptr_t)source_buffer, + efi_size_in_pages(source_size)); efi_delete_handle(*image_handle); *image_handle = NULL; free(info); @@ -1725,9 +1773,9 @@ error: * * Return: status code */ -static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, - efi_uintn_t *exit_data_size, - u16 **exit_data) +efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, + efi_uintn_t *exit_data_size, + u16 **exit_data) { struct efi_loaded_image_obj *image_obj = (struct efi_loaded_image_obj *)image_handle; diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index d94982314a..98c36e798f 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -910,9 +910,17 @@ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, return start; } -/* - * Helper to split a full device path (containing both device and file - * parts) into it's constituent parts. +/** + * efi_dp_split_file_path() - split of relative file path from device path + * + * Given a device path indicating a file on a device, separate the device + * path in two: the device path of the actual device and the file path + * relative to this device. + * + * @full_path: device path including device and file path + * @device_path: path of the device + * @file_path: relative path of the file + * Return: status code */ efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path, struct efi_device_path **device_path, @@ -929,7 +937,7 @@ efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path, while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) { p = efi_dp_next(p); if (!p) - return EFI_OUT_OF_RESOURCES; + return EFI_INVALID_PARAMETER; } fp = efi_dp_dup(p); if (!fp) diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index 4b4422205d..3a7323765b 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -641,6 +641,12 @@ static const struct efi_file_handle efi_file_handle_protocol = { .flush = efi_file_flush, }; +/** + * efi_file_from_path() - open file via device path + * + * @fp: device path + * @return: EFI_FILE_PROTOCOL for the file or NULL + */ struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp) { struct efi_simple_file_system_protocol *v; @@ -655,10 +661,14 @@ struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp) if (ret != EFI_SUCCESS) return NULL; - /* skip over device-path nodes before the file path: */ + /* Skip over device-path nodes before the file path. */ while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) fp = efi_dp_next(fp); + /* + * Step through the nodes of the directory path until the actual file + * node is reached which is the final node in the device path. + */ while (fp) { struct efi_device_path_file_path *fdp = container_of(fp, struct efi_device_path_file_path, dp); diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index a18ce0a570..fe66e7b9ff 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -42,8 +42,8 @@ static int machines[] = { #endif 0 }; -/* - * Print information about a loaded image. +/** + * efi_print_image_info() - print information about a loaded image * * If the program counter is located within the image the offset to the base * address is shown. @@ -51,7 +51,7 @@ static int machines[] = { * @obj: EFI object * @image: loaded image * @pc: program counter (use NULL to suppress offset output) - * @return: status code + * Return: status code */ static efi_status_t efi_print_image_info(struct efi_loaded_image_obj *obj, struct efi_loaded_image *image, @@ -69,8 +69,8 @@ static efi_status_t efi_print_image_info(struct efi_loaded_image_obj *obj, return EFI_SUCCESS; } -/* - * Print information about all loaded images. +/** + * efi_print_image_infos() - print information about all loaded images * * @pc: program counter (use NULL to suppress offset output) */ @@ -90,6 +90,15 @@ void efi_print_image_infos(void *pc) } } +/** + * efi_loader_relocate() - relocate UEFI binary + * + * @rel: pointer to the relocation table + * @rel_size: size of the relocation table in bytes + * @efi_reloc: actual load address of the image + * @pref_address: preferred load address of the image + * Return: status code + */ static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel, unsigned long rel_size, void *efi_reloc, unsigned long pref_address) @@ -102,7 +111,7 @@ static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel, return EFI_SUCCESS; end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size); - while (rel < end - 1 && rel->SizeOfBlock) { + while (rel < end && rel->SizeOfBlock) { const uint16_t *relocs = (const uint16_t *)(rel + 1); i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t); while (i--) { @@ -159,11 +168,12 @@ void __weak invalidate_icache_all(void) /* If the system doesn't support icache_all flush, cross our fingers */ } -/* - * Determine the memory types to be used for code and data. +/** + * efi_set_code_and_data_type() - determine the memory types to be used for code + * and data. * - * @loaded_image_info image descriptor - * @image_type field Subsystem of the optional header for + * @loaded_image_info: image descriptor + * @image_type: field Subsystem of the optional header for * Windows specific field */ static void efi_set_code_and_data_type( @@ -193,13 +203,19 @@ static void efi_set_code_and_data_type( } } -/* +/** + * efi_load_pe() - relocate EFI binary + * * This function loads all sections from a PE binary into a newly reserved - * piece of memory. On successful load it then returns the entry point for - * the binary. Otherwise NULL. + * piece of memory. On success the entry point is returned as handle->entry. + * + * @handle: loaded image handle + * @efi: pointer to the EFI binary + * @loaded_image_info: loaded image protocol + * Return: status code */ -void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, - struct efi_loaded_image *loaded_image_info) +efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, + struct efi_loaded_image *loaded_image_info) { IMAGE_NT_HEADERS32 *nt; IMAGE_DOS_HEADER *dos; @@ -210,7 +226,6 @@ void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, const IMAGE_BASE_RELOCATION *rel; unsigned long rel_size; int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC; - void *entry; uint64_t image_base; uint64_t image_size; unsigned long virt_size = 0; @@ -219,13 +234,13 @@ void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, dos = efi; if (dos->e_magic != IMAGE_DOS_SIGNATURE) { printf("%s: Invalid DOS Signature\n", __func__); - return NULL; + return EFI_LOAD_ERROR; } nt = (void *) ((char *)efi + dos->e_lfanew); if (nt->Signature != IMAGE_NT_SIGNATURE) { printf("%s: Invalid NT Signature\n", __func__); - return NULL; + return EFI_LOAD_ERROR; } for (i = 0; machines[i]; i++) @@ -237,7 +252,7 @@ void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, if (!supported) { printf("%s: Machine type 0x%04x is not supported\n", __func__, nt->FileHeader.Machine); - return NULL; + return EFI_LOAD_ERROR; } /* Calculate upper virtual address boundary */ @@ -263,9 +278,9 @@ void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, if (!efi_reloc) { printf("%s: Could not allocate %lu bytes\n", __func__, virt_size); - return NULL; + return EFI_OUT_OF_RESOURCES; } - entry = efi_reloc + opt->AddressOfEntryPoint; + handle->entry = efi_reloc + opt->AddressOfEntryPoint; rel_size = opt->DataDirectory[rel_idx].Size; rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress; virt_size = ALIGN(virt_size, opt->SectionAlignment); @@ -279,16 +294,16 @@ void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, if (!efi_reloc) { printf("%s: Could not allocate %lu bytes\n", __func__, virt_size); - return NULL; + return EFI_OUT_OF_RESOURCES; } - entry = efi_reloc + opt->AddressOfEntryPoint; + handle->entry = efi_reloc + opt->AddressOfEntryPoint; rel_size = opt->DataDirectory[rel_idx].Size; rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress; virt_size = ALIGN(virt_size, opt->SectionAlignment); } else { printf("%s: Invalid optional header magic %x\n", __func__, nt->OptionalHeader.Magic); - return NULL; + return EFI_LOAD_ERROR; } /* Load sections into RAM */ @@ -306,7 +321,7 @@ void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, (unsigned long)image_base) != EFI_SUCCESS) { efi_free_pages((uintptr_t) efi_reloc, (virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT); - return NULL; + return EFI_LOAD_ERROR; } /* Flush cache */ @@ -320,5 +335,5 @@ void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, handle->reloc_base = efi_reloc; handle->reloc_size = virt_size; - return entry; + return EFI_SUCCESS; } |