Merge branch 'master' of git://git.denx.de/u-boot-sunxi
[platform/kernel/u-boot.git] / lib / efi_loader / efi_boottime.c
index cc9efbb..bd8b8a1 100644 (file)
@@ -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;
@@ -1558,58 +1561,107 @@ efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
        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;
 }
 
@@ -1636,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;
@@ -1655,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);
@@ -1705,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;
@@ -1773,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
@@ -1783,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 */
@@ -2483,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);
 }
 
@@ -2825,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);