efi_loader: parameter checks in StartImage and Exit()
[oweals/u-boot.git] / lib / efi_loader / efi_boottime.c
index da978d2b34ab0e56ce397feb97b62c0ffba72eca..970c01614e2a523ab7c31e7450c2106dbf89060e 100644 (file)
@@ -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;
@@ -416,13 +428,12 @@ static efi_status_t EFIAPI efi_free_pool_ext(void *buffer)
  *
  * The protocols list is initialized. The object handle is set.
  */
-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 +451,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 +547,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);
 }
 
 /**
@@ -927,7 +938,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 +1030,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 +1063,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 +1072,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 +1083,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 +1101,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 +1128,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 +1139,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 +1156,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 +1167,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 +1187,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 +1217,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 +1281,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 +1289,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 +1300,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 +1372,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 +1501,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 +1520,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)
@@ -1489,12 +1537,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;
@@ -1506,7 +1549,7 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
                 * When asking for the device path interface, return
                 * bootefi_device_path
                 */
-               ret = efi_add_protocol(obj->parent.handle,
+               ret = efi_add_protocol(&obj->header,
                                       &efi_guid_device_path, device_path);
                if (ret != EFI_SUCCESS)
                        goto failure;
@@ -1516,63 +1559,110 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
         * 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;
 
+#if CONFIG_IS_ENABLED(EFI_LOADER_HII)
+       ret = efi_add_protocol(&obj->header,
+                              &efi_guid_hii_string_protocol,
+                              (void *)&efi_hii_string);
+       if (ret != EFI_SUCCESS)
+               goto failure;
+
+       ret = efi_add_protocol(&obj->header,
+                              &efi_guid_hii_database_protocol,
+                              (void *)&efi_hii_database);
+       if (ret != EFI_SUCCESS)
+               goto failure;
+
+       ret = efi_add_protocol(&obj->header,
+                              &efi_guid_hii_config_routing_protocol,
+                              (void *)&efi_hii_config_routing);
+       if (ret != EFI_SUCCESS)
+               goto failure;
+#endif
+
+       *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
  */
 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;
 }
 
@@ -1599,10 +1689,12 @@ 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;
        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 +1710,43 @@ 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, &dest_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);
-               if (ret != EFI_SUCCESS)
-                       goto error;
-       }
-       (*image_obj)->entry = efi_load_pe(*image_obj, source_buffer, info);
-       if (!(*image_obj)->entry) {
-               ret = EFI_UNSUPPORTED;
-               goto failure;
+               dest_buffer = source_buffer;
+               dp = file_path;
+               fp = NULL;
+       }
+       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);
        }
-       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
@@ -1785,6 +1770,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 +1838,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 +2179,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 +2282,8 @@ out:
  *
  * Return: status code
  */
-static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces(
-                       void **handle, ...)
+static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces
+                               (efi_handle_t *handle, ...)
 {
        EFI_ENTRY("%p", handle);
 
@@ -2316,7 +2319,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 +2342,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 +2361,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 +2388,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 +2429,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);
 }
 
@@ -2553,10 +2562,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 +2614,139 @@ 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;
+
+       /* 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;
+       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,
+                                   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;
+
+       /* 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 +2904,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 +2970,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;
        /*