X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=lib%2Fefi_loader%2Fefi_boottime.c;h=e5c46e9f0819b58e4dc80d422f8f44423daa550f;hb=90c2ebd2156982eee1af6faa00f6740e9f79b3c5;hp=fddfeb1a1a1616df8974efed6d656fae6347be68;hpb=eee6530ed1a8174d0f60e4c3c86bea3274c95951;p=oweals%2Fu-boot.git diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index fddfeb1a1a..e5c46e9f08 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -26,6 +26,17 @@ LIST_HEAD(efi_obj_list); /* List of all events */ LIST_HEAD(efi_events); +/* Handle of the currently executing image */ +static efi_handle_t current_image; + +/* + * If we're running on nasty systems (32bit ARM booting into non-EFI Linux) + * we need to do trickery with caches. Since we don't want to break the EFI + * aware boot path, only apply hacks when loading exiting directly (breaking + * direct Linux EFI booting along the way - oh well). + */ +static bool efi_is_direct_boot = true; + #ifdef CONFIG_ARM /* * The "gd" pointer lives in a register on ARM and AArch64 that we declare @@ -36,7 +47,8 @@ LIST_HEAD(efi_events); 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; @@ -411,18 +423,19 @@ static efi_status_t EFIAPI efi_free_pool_ext(void *buffer) } /** - * efi_add_handle() - add a new object to the object list - * @obj: object to be added + * efi_add_handle() - add a new handle to the object list + * + * @handle: handle to be added * - * The protocols list is initialized. The object handle is set. + * The protocols list is initialized. The handle is added to the list of known + * UEFI objects. */ -void efi_add_handle(struct efi_object *obj) +void efi_add_handle(efi_handle_t handle) { - if (!obj) + if (!handle) return; - INIT_LIST_HEAD(&obj->protocols); - obj->handle = obj; - list_add_tail(&obj->link, &efi_obj_list); + INIT_LIST_HEAD(&handle->protocols); + list_add_tail(&handle->link, &efi_obj_list); } /** @@ -440,7 +453,7 @@ efi_status_t efi_create_handle(efi_handle_t *handle) return EFI_OUT_OF_RESOURCES; efi_add_handle(obj); - *handle = obj->handle; + *handle = obj; return EFI_SUCCESS; } @@ -536,13 +549,13 @@ efi_status_t efi_remove_all_protocols(const efi_handle_t handle) * * @obj: handle to delete */ -void efi_delete_handle(struct efi_object *obj) +void efi_delete_handle(efi_handle_t handle) { - if (!obj) + if (!handle) return; - efi_remove_all_protocols(obj->handle); - list_del(&obj->link); - free(obj); + efi_remove_all_protocols(handle); + list_del(&handle->link); + free(handle); } /** @@ -607,7 +620,7 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, } if ((type & (EVT_NOTIFY_WAIT | EVT_NOTIFY_SIGNAL)) && - (is_valid_tpl(notify_tpl) != EFI_SUCCESS)) + (!notify_function || is_valid_tpl(notify_tpl) != EFI_SUCCESS)) return EFI_INVALID_PARAMETER; evt = calloc(1, sizeof(struct efi_event)); @@ -927,7 +940,7 @@ struct efi_object *efi_search_obj(const efi_handle_t handle) struct efi_object *efiobj; list_for_each_entry(efiobj, &efi_obj_list, link) { - if (efiobj->handle == handle) + if (efiobj == handle) return efiobj; } @@ -1019,7 +1032,7 @@ efi_status_t efi_add_protocol(const efi_handle_t handle, * Return: status code */ static efi_status_t EFIAPI efi_install_protocol_interface( - void **handle, const efi_guid_t *protocol, + efi_handle_t *handle, const efi_guid_t *protocol, int protocol_interface_type, void *protocol_interface) { efi_status_t r; @@ -1052,7 +1065,7 @@ out: /** * efi_get_drivers() - get all drivers associated to a controller - * @efiobj: handle of the controller + * @handle: handle of the controller * @protocol: protocol GUID (optional) * @number_of_drivers: number of child controllers * @driver_handle_buffer: handles of the the drivers @@ -1061,7 +1074,7 @@ out: * * Return: status code */ -static efi_status_t efi_get_drivers(struct efi_object *efiobj, +static efi_status_t efi_get_drivers(efi_handle_t handle, const efi_guid_t *protocol, efi_uintn_t *number_of_drivers, efi_handle_t **driver_handle_buffer) @@ -1072,7 +1085,7 @@ static efi_status_t efi_get_drivers(struct efi_object *efiobj, bool duplicate; /* Count all driver associations */ - list_for_each_entry(handler, &efiobj->protocols, link) { + list_for_each_entry(handler, &handle->protocols, link) { if (protocol && guidcmp(handler->guid, protocol)) continue; list_for_each_entry(item, &handler->open_infos, link) { @@ -1090,7 +1103,7 @@ static efi_status_t efi_get_drivers(struct efi_object *efiobj, if (!*driver_handle_buffer) return EFI_OUT_OF_RESOURCES; /* Collect unique driver handles */ - list_for_each_entry(handler, &efiobj->protocols, link) { + list_for_each_entry(handler, &handle->protocols, link) { if (protocol && guidcmp(handler->guid, protocol)) continue; list_for_each_entry(item, &handler->open_infos, link) { @@ -1117,7 +1130,7 @@ static efi_status_t efi_get_drivers(struct efi_object *efiobj, /** * efi_disconnect_all_drivers() - disconnect all drivers from a controller - * @efiobj: handle of the controller + * @handle: handle of the controller * @protocol: protocol GUID (optional) * @child_handle: handle of the child to destroy * @@ -1128,16 +1141,16 @@ static efi_status_t efi_get_drivers(struct efi_object *efiobj, * * Return: status code */ -static efi_status_t efi_disconnect_all_drivers( - struct efi_object *efiobj, - const efi_guid_t *protocol, - efi_handle_t child_handle) +static efi_status_t efi_disconnect_all_drivers + (efi_handle_t handle, + const efi_guid_t *protocol, + efi_handle_t child_handle) { efi_uintn_t number_of_drivers; efi_handle_t *driver_handle_buffer; efi_status_t r, ret; - ret = efi_get_drivers(efiobj, protocol, &number_of_drivers, + ret = efi_get_drivers(handle, protocol, &number_of_drivers, &driver_handle_buffer); if (ret != EFI_SUCCESS) return ret; @@ -1145,7 +1158,7 @@ static efi_status_t efi_disconnect_all_drivers( ret = EFI_NOT_FOUND; while (number_of_drivers) { r = EFI_CALL(efi_disconnect_controller( - efiobj->handle, + handle, driver_handle_buffer[--number_of_drivers], child_handle)); if (r == EFI_SUCCESS) @@ -1156,21 +1169,19 @@ static efi_status_t efi_disconnect_all_drivers( } /** - * efi_uninstall_protocol_interface() - uninstall protocol interface + * efi_uninstall_protocol() - uninstall protocol interface + * * @handle: handle from which the protocol shall be removed * @protocol: GUID of the protocol to be removed * @protocol_interface: interface to be removed * - * This function implements the UninstallProtocolInterface service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. + * This function DOES NOT delete a handle without installed protocol. * * Return: status code */ -static efi_status_t EFIAPI efi_uninstall_protocol_interface( - efi_handle_t handle, const efi_guid_t *protocol, - void *protocol_interface) +static efi_status_t efi_uninstall_protocol + (efi_handle_t handle, const efi_guid_t *protocol, + void *protocol_interface) { struct efi_object *efiobj; struct efi_handler *handler; @@ -1178,8 +1189,6 @@ static efi_status_t EFIAPI efi_uninstall_protocol_interface( struct efi_open_protocol_info_item *pos; efi_status_t r; - EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface); - /* Check handle */ efiobj = efi_search_obj(handle); if (!efiobj) { @@ -1210,7 +1219,41 @@ static efi_status_t EFIAPI efi_uninstall_protocol_interface( } r = efi_remove_protocol(handle, protocol, protocol_interface); out: - return EFI_EXIT(r); + return r; +} + +/** + * efi_uninstall_protocol_interface() - uninstall protocol interface + * @handle: handle from which the protocol shall be removed + * @protocol: GUID of the protocol to be removed + * @protocol_interface: interface to be removed + * + * This function implements the UninstallProtocolInterface service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: status code + */ +static efi_status_t EFIAPI efi_uninstall_protocol_interface + (efi_handle_t handle, const efi_guid_t *protocol, + void *protocol_interface) +{ + efi_status_t ret; + + EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface); + + ret = efi_uninstall_protocol(handle, protocol, protocol_interface); + if (ret != EFI_SUCCESS) + goto out; + + /* If the last protocol has been removed, delete the handle. */ + if (list_empty(&handle->protocols)) { + list_del(&handle->link); + free(handle); + } +out: + return EFI_EXIT(ret); } /** @@ -1240,7 +1283,7 @@ static efi_status_t EFIAPI efi_register_protocol_notify( * @search_type: selection criterion * @protocol: GUID of the protocol * @search_key: registration key - * @efiobj: handle + * @handle: handle * * See the documentation of the LocateHandle service in the UEFI specification. * @@ -1248,7 +1291,7 @@ static efi_status_t EFIAPI efi_register_protocol_notify( */ static int efi_search(enum efi_locate_search_type search_type, const efi_guid_t *protocol, void *search_key, - struct efi_object *efiobj) + efi_handle_t handle) { efi_status_t ret; @@ -1259,7 +1302,7 @@ static int efi_search(enum efi_locate_search_type search_type, /* TODO: RegisterProtocolNotify is not implemented yet */ return -1; case BY_PROTOCOL: - ret = efi_search_protocol(efiobj->handle, protocol, NULL); + ret = efi_search_protocol(handle, protocol, NULL); return (ret != EFI_SUCCESS); default: /* Invalid search type */ @@ -1331,7 +1374,7 @@ static efi_status_t efi_locate_handle( /* Then fill the array */ list_for_each_entry(efiobj, &efi_obj_list, link) { if (!efi_search(search_type, protocol, search_key, efiobj)) - *buffer++ = efiobj->handle; + *buffer++ = efiobj; } return EFI_SUCCESS; @@ -1460,15 +1503,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, @@ -1476,8 +1522,13 @@ 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; + struct efi_device_path *dp; + + /* 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) @@ -1489,12 +1540,7 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, } /* Add internal object to object list */ - efi_add_handle(&obj->parent); - - if (info_ptr) - *info_ptr = info; - if (handle_ptr) - *handle_ptr = obj; + efi_add_handle(&obj->header); info->revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION; info->file_path = file_path; @@ -1502,77 +1548,109 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, if (device_path) { info->device_handle = efi_dp_find_obj(device_path, NULL); - /* - * When asking for the device path interface, return - * bootefi_device_path - */ - ret = efi_add_protocol(obj->parent.handle, - &efi_guid_device_path, device_path); - if (ret != EFI_SUCCESS) + + dp = efi_dp_append(device_path, file_path); + if (!dp) { + ret = EFI_OUT_OF_RESOURCES; goto failure; + } + } else { + dp = NULL; } + ret = efi_add_protocol(&obj->header, + &efi_guid_loaded_image_device_path, dp); + if (ret != EFI_SUCCESS) + goto failure; /* * When asking for the loaded_image interface, just * return handle which points to loaded_image_info */ - ret = efi_add_protocol(obj->parent.handle, + ret = efi_add_protocol(&obj->header, &efi_guid_loaded_image, info); if (ret != EFI_SUCCESS) goto failure; + *info_ptr = info; + *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 */ +static 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; } @@ -1592,17 +1670,19 @@ error: * * Return: status code */ -static efi_status_t EFIAPI efi_load_image(bool boot_policy, - efi_handle_t parent_image, - struct efi_device_path *file_path, - void *source_buffer, - efi_uintn_t source_size, - efi_handle_t *image_handle) +efi_status_t EFIAPI efi_load_image(bool boot_policy, + efi_handle_t parent_image, + struct efi_device_path *file_path, + void *source_buffer, + efi_uintn_t source_size, + efi_handle_t *image_handle) { - struct efi_loaded_image *info; + 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; efi_status_t ret; + void *dest_buffer; EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image, file_path, source_buffer, source_size, image_handle); @@ -1618,150 +1698,35 @@ 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); - if (ret != EFI_SUCCESS) - goto failure; - /* - * 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); + ret = efi_load_image_from_path(file_path, &dest_buffer, + &source_size); if (ret != EFI_SUCCESS) goto error; + } else { + dest_buffer = source_buffer; + } + /* 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) + ret = efi_load_pe(*image_obj, dest_buffer, info); + if (!source_buffer) + /* Release buffer to which file was loaded */ + efi_free_pages((uintptr_t)dest_buffer, + efi_size_in_pages(source_size)); + if (ret == EFI_SUCCESS) { + info->system_table = &systab; + info->parent_handle = parent_image; + } else { + /* The image is invalid. Release all associated resources. */ + efi_delete_handle(*image_handle); + *image_handle = NULL; + free(info); } - (*image_obj)->entry = efi_load_pe(*image_obj, source_buffer, info); - if (!(*image_obj)->entry) { - ret = EFI_UNSUPPORTED; - goto failure; - } - info->system_table = &systab; - info->parent_handle = parent_image; - return EFI_EXIT(EFI_SUCCESS); -failure: - efi_delete_handle(*image_handle); - *image_handle = NULL; - free(info); error: return EFI_EXIT(ret); } -/** - * efi_start_image() - call the entry point of an image - * @image_handle: handle of the image - * @exit_data_size: size of the buffer - * @exit_data: buffer to receive the exit data of the called image - * - * This function implements the StartImage service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * Return: status code - */ -static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, - unsigned long *exit_data_size, - s16 **exit_data) -{ - struct efi_loaded_image_obj *image_obj = - (struct efi_loaded_image_obj *)image_handle; - efi_status_t ret; - - EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data); - - /* call the image! */ - if (setjmp(&image_obj->exit_jmp)) { - /* - * We called the entry point of the child image with EFI_CALL - * in the lines below. The child image called the Exit() boot - * service efi_exit() which executed the long jump that brought - * us to the current line. This implies that the second half - * of the EFI_CALL macro has not been executed. - */ -#ifdef CONFIG_ARM - /* - * efi_exit() called efi_restore_gd(). We have to undo this - * otherwise __efi_entry_check() will put the wrong value into - * app_gd. - */ - gd = app_gd; -#endif - /* - * To get ready to call EFI_EXIT below we have to execute the - * missed out steps of EFI_CALL. - */ - assert(__efi_entry_check()); - debug("%sEFI: %lu returned by started image\n", - __efi_nesting_dec(), - (unsigned long)((uintptr_t)image_obj->exit_status & - ~EFI_ERROR_MASK)); - return EFI_EXIT(image_obj->exit_status); - } - - ret = EFI_CALL(image_obj->entry(image_handle, &systab)); - - /* - * Usually UEFI applications call Exit() instead of returning. - * But because the world doesn't consist of ponies and unicorns, - * we're happy to emulate that behavior on behalf of a payload - * that forgot. - */ - return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL)); -} - -/** - * efi_exit() - leave an EFI application or driver - * @image_handle: handle of the application or driver that is exiting - * @exit_status: status code - * @exit_data_size: size of the buffer in bytes - * @exit_data: buffer with data describing an error - * - * This function implements the Exit service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * Return: status code - */ -static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, - efi_status_t exit_status, - unsigned long exit_data_size, - int16_t *exit_data) -{ - /* - * TODO: We should call the unload procedure of the loaded - * image protocol. - */ - struct efi_loaded_image_obj *image_obj = - (struct efi_loaded_image_obj *)image_handle; - - EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status, - exit_data_size, exit_data); - - /* Make sure entry/exit counts for EFI world cross-overs match */ - EFI_EXIT(exit_status); - - /* - * But longjmp out with the U-Boot gd, not the application's, as - * the other end is a setjmp call inside EFI context. - */ - efi_restore_gd(); - - image_obj->exit_status = exit_status; - longjmp(&image_obj->exit_jmp, 1); - - panic("EFI application exited"); -} - /** * efi_unload_image() - unload an EFI image * @image_handle: handle of the image to be unloaded @@ -1773,7 +1738,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, * * Return: status code */ -static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle) +efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle) { struct efi_object *efiobj; @@ -1785,6 +1750,21 @@ static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle) return EFI_EXIT(EFI_SUCCESS); } +/** + * efi_exit_caches() - fix up caches for EFI payloads if necessary + */ +static void efi_exit_caches(void) +{ +#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64) + /* + * Grub on 32bit ARM needs to have caches disabled before jumping into + * a zImage, but does not know of all cache layers. Give it a hand. + */ + if (efi_is_direct_boot) + cleanup_before_linux(); +#endif +} + /** * efi_exit_boot_services() - stop all boot services * @image_handle: handle of the loaded image @@ -1838,6 +1818,9 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, board_quiesce_devices(); + /* Fix up caches for EFI payloads if necessary */ + efi_exit_caches(); + /* This stops all lingering devices */ bootm_disable_interrupts(); @@ -2176,7 +2159,7 @@ static efi_status_t EFIAPI efi_locate_protocol(const efi_guid_t *protocol, efiobj = list_entry(lhandle, struct efi_object, link); - ret = efi_search_protocol(efiobj->handle, protocol, &handler); + ret = efi_search_protocol(efiobj, protocol, &handler); if (ret == EFI_SUCCESS) { *protocol_interface = handler->protocol_interface; return EFI_EXIT(EFI_SUCCESS); @@ -2279,8 +2262,8 @@ out: * * Return: status code */ -static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces( - void **handle, ...) +efi_status_t EFIAPI efi_install_multiple_protocol_interfaces + (efi_handle_t *handle, ...) { EFI_ENTRY("%p", handle); @@ -2316,7 +2299,7 @@ static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces( for (; i; --i) { protocol = efi_va_arg(argptr, efi_guid_t*); protocol_interface = efi_va_arg(argptr, void*); - EFI_CALL(efi_uninstall_protocol_interface(handle, protocol, + EFI_CALL(efi_uninstall_protocol_interface(*handle, protocol, protocol_interface)); } efi_va_end(argptr); @@ -2339,7 +2322,7 @@ static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces( * Return: status code */ static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces( - void *handle, ...) + efi_handle_t handle, ...) { EFI_ENTRY("%p", handle); @@ -2358,16 +2341,21 @@ static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces( if (!protocol) break; protocol_interface = efi_va_arg(argptr, void*); - r = EFI_CALL(efi_uninstall_protocol_interface( - handle, protocol, - protocol_interface)); + r = efi_uninstall_protocol(handle, protocol, + protocol_interface); if (r != EFI_SUCCESS) break; i++; } efi_va_end(argptr); - if (r == EFI_SUCCESS) + if (r == EFI_SUCCESS) { + /* If the last protocol has been removed, delete the handle. */ + if (list_empty(&handle->protocols)) { + list_del(&handle->link); + free(handle); + } return EFI_EXIT(r); + } /* If an error occurred undo all changes. */ efi_va_start(argptr, handle); @@ -2380,7 +2368,8 @@ static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces( } efi_va_end(argptr); - return EFI_EXIT(r); + /* In case of an error always return EFI_INVALID_PARAMETER */ + return EFI_EXIT(EFI_INVALID_PARAMETER); } /** @@ -2420,7 +2409,7 @@ static void EFIAPI efi_copy_mem(void *destination, const void *source, size_t length) { EFI_ENTRY("%p, %p, %ld", destination, source, (unsigned long)length); - memcpy(destination, source, length); + memmove(destination, source, length); EFI_EXIT(EFI_SUCCESS); } @@ -2506,7 +2495,7 @@ static efi_status_t efi_protocol_open( if (item->info.attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) opened_by_driver = true; } - /* Only one controller can be conncected */ + /* Only one controller can be connected */ if (opened_by_driver) return EFI_ACCESS_DENIED; } @@ -2553,10 +2542,10 @@ out: * * Return: status code */ -static efi_status_t EFIAPI efi_open_protocol( - void *handle, const efi_guid_t *protocol, - void **protocol_interface, void *agent_handle, - void *controller_handle, uint32_t attributes) +static efi_status_t EFIAPI efi_open_protocol + (efi_handle_t handle, const efi_guid_t *protocol, + void **protocol_interface, efi_handle_t agent_handle, + efi_handle_t controller_handle, uint32_t attributes) { struct efi_handler *handler; efi_status_t r = EFI_INVALID_PARAMETER; @@ -2605,6 +2594,187 @@ out: return EFI_EXIT(r); } +/** + * efi_start_image() - call the entry point of an image + * @image_handle: handle of the image + * @exit_data_size: size of the buffer + * @exit_data: buffer to receive the exit data of the called image + * + * This function implements the StartImage service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: status code + */ +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; + efi_status_t ret; + void *info; + efi_handle_t parent_image = current_image; + + EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data); + + /* Check parameters */ + ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image, + &info, NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL)); + if (ret != EFI_SUCCESS) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + efi_is_direct_boot = false; + + image_obj->exit_data_size = exit_data_size; + image_obj->exit_data = exit_data; + + /* call the image! */ + if (setjmp(&image_obj->exit_jmp)) { + /* + * We called the entry point of the child image with EFI_CALL + * in the lines below. The child image called the Exit() boot + * service efi_exit() which executed the long jump that brought + * us to the current line. This implies that the second half + * of the EFI_CALL macro has not been executed. + */ +#ifdef CONFIG_ARM + /* + * efi_exit() called efi_restore_gd(). We have to undo this + * otherwise __efi_entry_check() will put the wrong value into + * app_gd. + */ + gd = app_gd; +#endif + /* + * To get ready to call EFI_EXIT below we have to execute the + * missed out steps of EFI_CALL. + */ + assert(__efi_entry_check()); + debug("%sEFI: %lu returned by started image\n", + __efi_nesting_dec(), + (unsigned long)((uintptr_t)image_obj->exit_status & + ~EFI_ERROR_MASK)); + current_image = parent_image; + return EFI_EXIT(image_obj->exit_status); + } + + current_image = image_handle; + EFI_PRINT("Jumping into 0x%p\n", image_obj->entry); + ret = EFI_CALL(image_obj->entry(image_handle, &systab)); + + /* + * Usually UEFI applications call Exit() instead of returning. + * But because the world doesn't consist of ponies and unicorns, + * we're happy to emulate that behavior on behalf of a payload + * that forgot. + */ + return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL)); +} + +/** + * efi_update_exit_data() - fill exit data parameters of StartImage() + * + * @image_obj image handle + * @exit_data_size size of the exit data buffer + * @exit_data buffer with data returned by UEFI payload + * Return: status code + */ +static efi_status_t efi_update_exit_data(struct efi_loaded_image_obj *image_obj, + efi_uintn_t exit_data_size, + u16 *exit_data) +{ + efi_status_t ret; + + /* + * If exit_data is not provided to StartImage(), exit_data_size must be + * ignored. + */ + if (!image_obj->exit_data) + return EFI_SUCCESS; + if (image_obj->exit_data_size) + *image_obj->exit_data_size = exit_data_size; + if (exit_data_size && exit_data) { + ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, + exit_data_size, + (void **)image_obj->exit_data); + if (ret != EFI_SUCCESS) + return ret; + memcpy(*image_obj->exit_data, exit_data, exit_data_size); + } else { + image_obj->exit_data = NULL; + } + return EFI_SUCCESS; +} + +/** + * efi_exit() - leave an EFI application or driver + * @image_handle: handle of the application or driver that is exiting + * @exit_status: status code + * @exit_data_size: size of the buffer in bytes + * @exit_data: buffer with data describing an error + * + * This function implements the Exit service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: status code + */ +static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, + efi_status_t exit_status, + efi_uintn_t exit_data_size, + u16 *exit_data) +{ + /* + * TODO: We should call the unload procedure of the loaded + * image protocol. + */ + efi_status_t ret; + void *info; + struct efi_loaded_image_obj *image_obj = + (struct efi_loaded_image_obj *)image_handle; + + EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status, + exit_data_size, exit_data); + + /* Check parameters */ + if (image_handle != current_image) + goto out; + ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image, + &info, NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL)); + if (ret != EFI_SUCCESS) + goto out; + + /* Exit data is only foreseen in case of failure. */ + if (exit_status != EFI_SUCCESS) { + ret = efi_update_exit_data(image_obj, exit_data_size, + exit_data); + /* Exiting has priority. Don't return error to caller. */ + if (ret != EFI_SUCCESS) + EFI_PRINT("%s: out of memory\n", __func__); + } + + /* Make sure entry/exit counts for EFI world cross-overs match */ + EFI_EXIT(exit_status); + + /* + * But longjmp out with the U-Boot gd, not the application's, as + * the other end is a setjmp call inside EFI context. + */ + efi_restore_gd(); + + image_obj->exit_status = exit_status; + longjmp(&image_obj->exit_jmp, 1); + + panic("EFI application exited"); +out: + return EFI_EXIT(EFI_INVALID_PARAMETER); +} + /** * efi_handle_protocol() - get interface of a protocol on a handle * @handle: handle on which the protocol shall be opened @@ -2762,7 +2932,7 @@ static efi_status_t EFIAPI efi_connect_controller( efi_status_t ret = EFI_NOT_FOUND; struct efi_object *efiobj; - EFI_ENTRY("%p, %p, %p, %d", controller_handle, driver_image_handle, + EFI_ENTRY("%p, %p, %pD, %d", controller_handle, driver_image_handle, remain_device_path, recursive); efiobj = efi_search_obj(controller_handle); @@ -2828,13 +2998,19 @@ static efi_status_t EFIAPI efi_reinstall_protocol_interface( EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, old_interface, new_interface); - ret = EFI_CALL(efi_uninstall_protocol_interface(handle, protocol, - old_interface)); + + /* Uninstall protocol but do not delete handle */ + ret = efi_uninstall_protocol(handle, protocol, old_interface); if (ret != EFI_SUCCESS) goto out; - ret = EFI_CALL(efi_install_protocol_interface(&handle, protocol, - EFI_NATIVE_INTERFACE, - new_interface)); + + /* Install the new protocol */ + ret = efi_add_protocol(handle, protocol, new_interface); + /* + * The UEFI spec does not specify what should happen to the handle + * if in case of an error no protocol interface remains on the handle. + * So let's do nothing here. + */ if (ret != EFI_SUCCESS) goto out; /*