Merge branch 'master' of git://git.denx.de/u-boot-sunxi
[platform/kernel/u-boot.git] / lib / efi_loader / efi_boottime.c
index 3976470..bd8b8a1 100644 (file)
@@ -26,6 +26,14 @@ LIST_HEAD(efi_obj_list);
 /* List of all events */
 LIST_HEAD(efi_events);
 
+/*
+ * 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 +44,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;
@@ -1489,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,
@@ -1505,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)
@@ -1518,12 +1534,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;
@@ -1535,7 +1546,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,
+               ret = efi_add_protocol(&obj->header,
                                       &efi_guid_device_path, device_path);
                if (ret != EFI_SUCCESS)
                        goto failure;
@@ -1545,63 +1556,112 @@ 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,
+       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
+
+       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;
 }
 
@@ -1628,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;
@@ -1647,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);
@@ -1697,9 +1773,9 @@ error:
  *
  * Return: status code
  */
-static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
-                                          unsigned long *exit_data_size,
-                                          s16 **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;
@@ -1707,6 +1783,8 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
 
        EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
 
+       efi_is_direct_boot = false;
+
        /* call the image! */
        if (setjmp(&image_obj->exit_jmp)) {
                /*
@@ -1763,8 +1841,8 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
  */
 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)
+                                   efi_uintn_t exit_data_size,
+                                   u16 *exit_data)
 {
        /*
         * TODO: We should call the unload procedure of the loaded
@@ -1773,7 +1851,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
        struct efi_loaded_image_obj *image_obj =
                (struct efi_loaded_image_obj *)image_handle;
 
-       EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status,
+       EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
                  exit_data_size, exit_data);
 
        /* Make sure entry/exit counts for EFI world cross-overs match */
@@ -1815,6 +1893,21 @@ static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
 }
 
 /**
+ * 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
  * @map_key:      key of the memory map
@@ -1867,6 +1960,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();
 
@@ -2414,7 +2510,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);
 }
 
 /**
@@ -2454,7 +2551,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);
 }
 
@@ -2796,7 +2893,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);