Merge tag 'efi-2019-04-rc2' of https://github.com/xypron2/u-boot

The patches fix multiple errors. Mentionable are:
- EFI unit tests (bootefi selftest) can run on i386.
- `make tests` executes the Unicode unit tests.

The LoadImage patch is preparing for further rework to be delivered
in v2019.07.
diff --git a/Documentation/efi.rst b/Documentation/efi.rst
index 51c1de2..5337a55 100644
--- a/Documentation/efi.rst
+++ b/Documentation/efi.rst
@@ -9,6 +9,12 @@
 .. kernel-doc:: lib/efi_loader/efi_boottime.c
    :internal:
 
+Image relocation
+~~~~~~~~~~~~~~~~
+
+.. kernel-doc:: lib/efi_loader/efi_image_loader.c
+   :internal:
+
 Runtime services
 ----------------
 
diff --git a/cmd/bootefi.c b/cmd/bootefi.c
index ee685d8..3619a20 100644
--- a/cmd/bootefi.c
+++ b/cmd/bootefi.c
@@ -133,20 +133,6 @@
 	return ret;
 }
 
-static efi_status_t efi_do_enter(
-			efi_handle_t image_handle, struct efi_system_table *st,
-			EFIAPI efi_status_t (*entry)(
-				efi_handle_t image_handle,
-				struct efi_system_table *st))
-{
-	efi_status_t ret = EFI_LOAD_ERROR;
-
-	if (entry)
-		ret = entry(image_handle, st);
-	st->boottime->exit(image_handle, ret, 0, NULL);
-	return ret;
-}
-
 /*
  * efi_carve_out_dt_rsv() - Carve out DT reserved memory ranges
  *
@@ -266,9 +252,6 @@
 	struct efi_loaded_image_obj *image_obj = NULL;
 	struct efi_loaded_image *loaded_image_info = NULL;
 
-	EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
-				     struct efi_system_table *st);
-
 	/*
 	 * Special case for efi payload not loaded from disk, such as
 	 * 'bootefi hello' or for example payload loaded directly into
@@ -300,11 +283,9 @@
 		goto err_prepare;
 
 	/* Load the EFI payload */
-	entry = efi_load_pe(image_obj, efi, loaded_image_info);
-	if (!entry) {
-		ret = EFI_LOAD_ERROR;
+	ret = efi_load_pe(image_obj, efi, loaded_image_info);
+	if (ret != EFI_SUCCESS)
 		goto err_prepare;
-	}
 
 	if (memdp) {
 		struct efi_device_path_memory *mdp = (void *)memdp;
@@ -319,14 +300,8 @@
 		"{ro,boot}(blob)0000000000000000");
 
 	/* Call our payload! */
-	debug("%s: Jumping to 0x%p\n", __func__, entry);
-
-	if (setjmp(&image_obj->exit_jmp)) {
-		ret = image_obj->exit_status;
-		goto err_prepare;
-	}
-
-	ret = efi_do_enter(&image_obj->header, &systab, entry);
+	debug("%s: Jumping to 0x%p\n", __func__, image_obj->entry);
+	ret = EFI_CALL(efi_start_image(&image_obj->header, NULL, NULL));
 
 err_prepare:
 	/* image has returned, loaded-image obj goes *poof*: */
@@ -343,38 +318,46 @@
 /**
  * bootefi_test_prepare() - prepare to run an EFI test
  *
- * This sets things up so we can call EFI functions. This involves preparing
- * the 'gd' pointer and setting up the load ed image data structures.
+ * Prepare to run a test as if it were provided by a loaded image.
  *
- * @image_objp: loaded_image_infop: Pointer to a struct which will hold the
- *    loaded image object. This struct will be inited by this function before
- *    use.
- * @loaded_image_infop: Pointer to a struct which will hold the loaded image
- *    info. This struct will be inited by this function before use.
- * @path: File path to the test being run (often just the test name with a
- *    backslash before it
- * @test_func: Address of the test function that is being run
- * @load_options_path: U-Boot environment variable to use as load options
- * @return 0 if OK, -ve on error
+ * @image_objp:		pointer to be set to the loaded image handle
+ * @loaded_image_infop:	pointer to be set to the loaded image protocol
+ * @path:		dummy file path used to construct the device path
+ *			set in the loaded image protocol
+ * @load_options_path:	name of a U-Boot environment variable. Its value is
+ *			set as load options in the loaded image protocol.
+ * Return:		status code
  */
 static efi_status_t bootefi_test_prepare
 		(struct efi_loaded_image_obj **image_objp,
-		struct efi_loaded_image **loaded_image_infop, const char *path,
-		ulong test_func, const char *load_options_path)
+		 struct efi_loaded_image **loaded_image_infop, const char *path,
+		 const char *load_options_path)
 {
+	efi_status_t ret;
+
 	/* Construct a dummy device path */
-	bootefi_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE,
-					      (uintptr_t)test_func,
-					      (uintptr_t)test_func);
+	bootefi_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, 0, 0);
 	if (!bootefi_device_path)
 		return EFI_OUT_OF_RESOURCES;
+
 	bootefi_image_path = efi_dp_from_file(NULL, 0, path);
-	if (!bootefi_image_path)
-		return EFI_OUT_OF_RESOURCES;
+	if (!bootefi_image_path) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto failure;
+	}
 
-	return bootefi_run_prepare(load_options_path, bootefi_device_path,
-				   bootefi_image_path, image_objp,
-				   loaded_image_infop);
+	ret = bootefi_run_prepare(load_options_path, bootefi_device_path,
+				  bootefi_image_path, image_objp,
+				  loaded_image_infop);
+	if (ret == EFI_SUCCESS)
+		return ret;
+
+	efi_free_pool(bootefi_image_path);
+	bootefi_image_path = NULL;
+failure:
+	efi_free_pool(bootefi_device_path);
+	bootefi_device_path = NULL;
+	return ret;
 }
 
 #endif /* CONFIG_CMD_BOOTEFI_SELFTEST */
@@ -456,13 +439,13 @@
 		struct efi_loaded_image_obj *image_obj;
 		struct efi_loaded_image *loaded_image_info;
 
-		if (bootefi_test_prepare(&image_obj, &loaded_image_info,
-					 "\\selftest", (uintptr_t)&efi_selftest,
-					 "efi_selftest"))
+		r = bootefi_test_prepare(&image_obj, &loaded_image_info,
+					 "\\selftest", "efi_selftest");
+		if (r != EFI_SUCCESS)
 			return CMD_RET_FAILURE;
 
 		/* Execute the test */
-		r = efi_selftest(&image_obj->header, &systab);
+		r = EFI_CALL(efi_selftest(&image_obj->header, &systab));
 		bootefi_run_finish(image_obj, loaded_image_info);
 		return r != EFI_SUCCESS;
 	} else
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 9dd933d..512880a 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -301,8 +301,8 @@
 /* Called from places to check whether a timer expired */
 void efi_timer_check(void);
 /* PE loader implementation */
-void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
-		  struct efi_loaded_image *loaded_image_info);
+efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
+			 struct efi_loaded_image *loaded_image_info);
 /* Called once to store the pristine gd pointer */
 void efi_save_gd(void);
 /* Special case handler for error/abort that just tries to dtrt to get
@@ -320,6 +320,10 @@
 void efi_delete_handle(efi_handle_t obj);
 /* Call this to validate a handle and find the EFI object for it */
 struct efi_object *efi_search_obj(const efi_handle_t handle);
+/* Start image */
+efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
+				    efi_uintn_t *exit_data_size,
+				    u16 **exit_data);
 /* Find a protocol on a handle */
 efi_status_t efi_search_protocol(const efi_handle_t handle,
 				 const efi_guid_t *protocol_guid,
@@ -397,7 +401,7 @@
 				    struct efi_loaded_image_obj **handle_ptr,
 				    struct efi_loaded_image **info_ptr);
 efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
-				      void **buffer);
+				      void **buffer, efi_uintn_t *size);
 /* Print information about all loaded images */
 void efi_print_image_infos(void *pc);
 
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index a095df3..196116b 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -150,7 +150,7 @@
 		debug("%s: trying to load \"%ls\" from %pD\n",
 		      __func__, lo.label, lo.file_path);
 
-		ret = efi_load_image_from_path(lo.file_path, &image);
+		ret = efi_load_image_from_path(lo.file_path, &image, &size);
 
 		if (ret != EFI_SUCCESS)
 			goto error;
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index f74f989..bd8b8a1 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -44,7 +44,8 @@
 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 @@
 
 /**
  * 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 @@
 				    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 @@
 	/* Add internal object to object list */
 	efi_add_handle(&obj->header);
 
-	if (info_ptr)
-		*info_ptr = info;
-	if (handle_ptr)
-		*handle_ptr = obj;
-
 	info->revision =  EFI_LOADED_IMAGE_PROTOCOL_REVISION;
 	info->file_path = file_path;
 	info->system_table = &systab;
@@ -1578,58 +1581,87 @@
 		goto failure;
 #endif
 
+	if (info_ptr)
+		*info_ptr = info;
+	if (handle_ptr)
+		*handle_ptr = obj;
+
 	return ret;
 failure:
 	printf("ERROR: Failure to install protocols for loaded image\n");
+	efi_delete_handle(&obj->header);
+	free(info);
 	return ret;
 }
 
 /**
  * efi_load_image_from_path() - load an image using a file path
- * @file_path: the path of the image to load
- * @buffer:    buffer containing the loaded image
  *
- * Return: status code
+ * Read a file into a buffer allocated as EFI_BOOT_SERVICES_DATA. It is the
+ * callers obligation to update the memory type as needed.
+ *
+ * @file_path:	the path of the image to load
+ * @buffer:	buffer containing the loaded image
+ * @size:	size of the loaded image
+ * Return:	status code
  */
 efi_status_t efi_load_image_from_path(struct efi_device_path *file_path,
-				      void **buffer)
+				      void **buffer, efi_uintn_t *size)
 {
 	struct efi_file_info *info = NULL;
 	struct efi_file_handle *f;
 	static efi_status_t ret;
+	u64 addr;
 	efi_uintn_t bs;
 
+	/* In case of failure nothing is returned */
+	*buffer = NULL;
+	*size = 0;
+
+	/* Open file */
 	f = efi_file_from_path(file_path);
 	if (!f)
 		return EFI_DEVICE_ERROR;
 
+	/* Get file size */
 	bs = 0;
 	EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
 				  &bs, info));
-	if (ret == EFI_BUFFER_TOO_SMALL) {
-		info = malloc(bs);
-		EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid,
-					  &bs, info));
-	}
-	if (ret != EFI_SUCCESS)
+	if (ret != EFI_BUFFER_TOO_SMALL) {
+		ret =  EFI_DEVICE_ERROR;
 		goto error;
+	}
 
-	ret = efi_allocate_pool(EFI_LOADER_DATA, info->file_size, buffer);
-	if (ret)
+	info = malloc(bs);
+	EFI_CALL(ret = f->getinfo(f, (efi_guid_t *)&efi_file_info_guid, &bs,
+				  info));
+	if (ret != EFI_SUCCESS)
 		goto error;
 
+	/*
+	 * When reading the file we do not yet know if it contains an
+	 * application, a boottime driver, or a runtime driver. So here we
+	 * allocate a buffer as EFI_BOOT_SERVICES_DATA. The caller has to
+	 * update the reservation according to the image type.
+	 */
 	bs = info->file_size;
-	EFI_CALL(ret = f->read(f, &bs, *buffer));
-
-error:
-	free(info);
-	EFI_CALL(f->close(f));
-
+	ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+				 EFI_BOOT_SERVICES_DATA,
+				 efi_size_in_pages(bs), &addr);
 	if (ret != EFI_SUCCESS) {
-		efi_free_pool(*buffer);
-		*buffer = NULL;
+		ret = EFI_OUT_OF_RESOURCES;
+		goto error;
 	}
 
+	/* Read file */
+	EFI_CALL(ret = f->read(f, &bs, (void *)(uintptr_t)addr));
+	if (ret != EFI_SUCCESS)
+		efi_free_pages(addr, efi_size_in_pages(bs));
+	*buffer = (void *)(uintptr_t)addr;
+	*size = bs;
+error:
+	EFI_CALL(f->close(f));
+	free(info);
 	return ret;
 }
 
@@ -1656,6 +1688,7 @@
 					  efi_uintn_t source_size,
 					  efi_handle_t *image_handle)
 {
+	struct efi_device_path *dp, *fp;
 	struct efi_loaded_image *info = NULL;
 	struct efi_loaded_image_obj **image_obj =
 		(struct efi_loaded_image_obj **)image_handle;
@@ -1675,36 +1708,51 @@
 	}
 
 	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;
-	}
-	(*image_obj)->entry = efi_load_pe(*image_obj, source_buffer, info);
-	if (!(*image_obj)->entry) {
-		ret = EFI_UNSUPPORTED;
-		goto failure;
+		dest_buffer = (void *)(uintptr_t)addr;
+		memcpy(dest_buffer, source_buffer, source_size);
+		source_buffer = dest_buffer;
+
+		dp = file_path;
+		fp = NULL;
 	}
+	ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
+	if (ret != EFI_SUCCESS)
+		goto error_invalid_image;
+	ret = efi_load_pe(*image_obj, source_buffer, info);
+	if (ret != EFI_SUCCESS)
+		goto error_invalid_image;
+	/* Update the type of the allocated memory */
+	efi_add_memory_map((uintptr_t)source_buffer,
+			   efi_size_in_pages(source_size),
+			   info->image_code_type, false);
 	info->system_table = &systab;
 	info->parent_handle = parent_image;
 	return EFI_EXIT(EFI_SUCCESS);
-failure:
+error_invalid_image:
+	/* The image is invalid. Release all associated resources. */
+	efi_free_pages((uintptr_t)source_buffer,
+		       efi_size_in_pages(source_size));
 	efi_delete_handle(*image_handle);
 	*image_handle = NULL;
 	free(info);
@@ -1725,9 +1773,9 @@
  *
  * Return: status code
  */
-static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
-					   efi_uintn_t *exit_data_size,
-					   u16 **exit_data)
+efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
+				    efi_uintn_t *exit_data_size,
+				    u16 **exit_data)
 {
 	struct efi_loaded_image_obj *image_obj =
 		(struct efi_loaded_image_obj *)image_handle;
diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c
index d949823..98c36e7 100644
--- a/lib/efi_loader/efi_device_path.c
+++ b/lib/efi_loader/efi_device_path.c
@@ -910,9 +910,17 @@
 	return start;
 }
 
-/*
- * Helper to split a full device path (containing both device and file
- * parts) into it's constituent parts.
+/**
+ * efi_dp_split_file_path() - split of relative file path from device path
+ *
+ * Given a device path indicating a file on a device, separate the device
+ * path in two: the device path of the actual device and the file path
+ * relative to this device.
+ *
+ * @full_path:		device path including device and file path
+ * @device_path:	path of the device
+ * @file_path:		relative path of the file
+ * Return:		status code
  */
 efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path,
 				    struct efi_device_path **device_path,
@@ -929,7 +937,7 @@
 	while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) {
 		p = efi_dp_next(p);
 		if (!p)
-			return EFI_OUT_OF_RESOURCES;
+			return EFI_INVALID_PARAMETER;
 	}
 	fp = efi_dp_dup(p);
 	if (!fp)
diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
index 4b44222..3a73237 100644
--- a/lib/efi_loader/efi_file.c
+++ b/lib/efi_loader/efi_file.c
@@ -641,6 +641,12 @@
 	.flush = efi_file_flush,
 };
 
+/**
+ * efi_file_from_path() - open file via device path
+ *
+ * @fp:		device path
+ * @return:	EFI_FILE_PROTOCOL for the file or NULL
+ */
 struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
 {
 	struct efi_simple_file_system_protocol *v;
@@ -655,10 +661,14 @@
 	if (ret != EFI_SUCCESS)
 		return NULL;
 
-	/* skip over device-path nodes before the file path: */
+	/* Skip over device-path nodes before the file path. */
 	while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
 		fp = efi_dp_next(fp);
 
+	/*
+	 * Step through the nodes of the directory path until the actual file
+	 * node is reached which is the final node in the device path.
+	 */
 	while (fp) {
 		struct efi_device_path_file_path *fdp =
 			container_of(fp, struct efi_device_path_file_path, dp);
diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c
index a18ce0a..fe66e7b 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -42,8 +42,8 @@
 #endif
 	0 };
 
-/*
- * Print information about a loaded image.
+/**
+ * efi_print_image_info() - print information about a loaded image
  *
  * If the program counter is located within the image the offset to the base
  * address is shown.
@@ -51,7 +51,7 @@
  * @obj:	EFI object
  * @image:	loaded image
  * @pc:		program counter (use NULL to suppress offset output)
- * @return:	status code
+ * Return:	status code
  */
 static efi_status_t efi_print_image_info(struct efi_loaded_image_obj *obj,
 					 struct efi_loaded_image *image,
@@ -69,8 +69,8 @@
 	return EFI_SUCCESS;
 }
 
-/*
- * Print information about all loaded images.
+/**
+ * efi_print_image_infos() - print information about all loaded images
  *
  * @pc:		program counter (use NULL to suppress offset output)
  */
@@ -90,6 +90,15 @@
 	}
 }
 
+/**
+ * efi_loader_relocate() - relocate UEFI binary
+ *
+ * @rel:		pointer to the relocation table
+ * @rel_size:		size of the relocation table in bytes
+ * @efi_reloc:		actual load address of the image
+ * @pref_address:	preferred load address of the image
+ * Return:		status code
+ */
 static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
 			unsigned long rel_size, void *efi_reloc,
 			unsigned long pref_address)
@@ -102,7 +111,7 @@
 		return EFI_SUCCESS;
 
 	end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
-	while (rel < end - 1 && rel->SizeOfBlock) {
+	while (rel < end && rel->SizeOfBlock) {
 		const uint16_t *relocs = (const uint16_t *)(rel + 1);
 		i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
 		while (i--) {
@@ -159,11 +168,12 @@
 	/* If the system doesn't support icache_all flush, cross our fingers */
 }
 
-/*
- * Determine the memory types to be used for code and data.
+/**
+ * efi_set_code_and_data_type() - determine the memory types to be used for code
+ *				  and data.
  *
- * @loaded_image_info	image descriptor
- * @image_type		field Subsystem of the optional header for
+ * @loaded_image_info:	image descriptor
+ * @image_type:		field Subsystem of the optional header for
  *			Windows specific field
  */
 static void efi_set_code_and_data_type(
@@ -193,13 +203,19 @@
 	}
 }
 
-/*
+/**
+ * efi_load_pe() - relocate EFI binary
+ *
  * This function loads all sections from a PE binary into a newly reserved
- * piece of memory. On successful load it then returns the entry point for
- * the binary. Otherwise NULL.
+ * piece of memory. On success the entry point is returned as handle->entry.
+ *
+ * @handle:		loaded image handle
+ * @efi:		pointer to the EFI binary
+ * @loaded_image_info:	loaded image protocol
+ * Return:		status code
  */
-void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
-		  struct efi_loaded_image *loaded_image_info)
+efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi,
+			 struct efi_loaded_image *loaded_image_info)
 {
 	IMAGE_NT_HEADERS32 *nt;
 	IMAGE_DOS_HEADER *dos;
@@ -210,7 +226,6 @@
 	const IMAGE_BASE_RELOCATION *rel;
 	unsigned long rel_size;
 	int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC;
-	void *entry;
 	uint64_t image_base;
 	uint64_t image_size;
 	unsigned long virt_size = 0;
@@ -219,13 +234,13 @@
 	dos = efi;
 	if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
 		printf("%s: Invalid DOS Signature\n", __func__);
-		return NULL;
+		return EFI_LOAD_ERROR;
 	}
 
 	nt = (void *) ((char *)efi + dos->e_lfanew);
 	if (nt->Signature != IMAGE_NT_SIGNATURE) {
 		printf("%s: Invalid NT Signature\n", __func__);
-		return NULL;
+		return EFI_LOAD_ERROR;
 	}
 
 	for (i = 0; machines[i]; i++)
@@ -237,7 +252,7 @@
 	if (!supported) {
 		printf("%s: Machine type 0x%04x is not supported\n",
 		       __func__, nt->FileHeader.Machine);
-		return NULL;
+		return EFI_LOAD_ERROR;
 	}
 
 	/* Calculate upper virtual address boundary */
@@ -263,9 +278,9 @@
 		if (!efi_reloc) {
 			printf("%s: Could not allocate %lu bytes\n",
 			       __func__, virt_size);
-			return NULL;
+			return EFI_OUT_OF_RESOURCES;
 		}
-		entry = efi_reloc + opt->AddressOfEntryPoint;
+		handle->entry = efi_reloc + opt->AddressOfEntryPoint;
 		rel_size = opt->DataDirectory[rel_idx].Size;
 		rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
 		virt_size = ALIGN(virt_size, opt->SectionAlignment);
@@ -279,16 +294,16 @@
 		if (!efi_reloc) {
 			printf("%s: Could not allocate %lu bytes\n",
 			       __func__, virt_size);
-			return NULL;
+			return EFI_OUT_OF_RESOURCES;
 		}
-		entry = efi_reloc + opt->AddressOfEntryPoint;
+		handle->entry = efi_reloc + opt->AddressOfEntryPoint;
 		rel_size = opt->DataDirectory[rel_idx].Size;
 		rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
 		virt_size = ALIGN(virt_size, opt->SectionAlignment);
 	} else {
 		printf("%s: Invalid optional header magic %x\n", __func__,
 		       nt->OptionalHeader.Magic);
-		return NULL;
+		return EFI_LOAD_ERROR;
 	}
 
 	/* Load sections into RAM */
@@ -306,7 +321,7 @@
 				(unsigned long)image_base) != EFI_SUCCESS) {
 		efi_free_pages((uintptr_t) efi_reloc,
 			       (virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT);
-		return NULL;
+		return EFI_LOAD_ERROR;
 	}
 
 	/* Flush cache */
@@ -320,5 +335,5 @@
 	handle->reloc_base = efi_reloc;
 	handle->reloc_size = virt_size;
 
-	return entry;
+	return EFI_SUCCESS;
 }
diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile
index 7f4eafb..c9720c9 100644
--- a/lib/efi_selftest/Makefile
+++ b/lib/efi_selftest/Makefile
@@ -51,6 +51,7 @@
 ifeq ($(CONFIG_SANDBOX)$(CONFIG_CPU_V7M)$(CONFIG_X86_64),)
 
 obj-y += \
+efi_selftest_loadimage.o \
 efi_selftest_startimage_exit.o \
 efi_selftest_startimage_return.o
 
@@ -68,6 +69,8 @@
 	$(obj)/../../tools/file2include $(obj)/efi_selftest_miniapp_return.efi > \
 	$(obj)/efi_miniapp_file_image_return.h
 
+$(obj)/efi_selftest_loadimage.o: $(obj)/efi_miniapp_file_image_exit.h
+
 $(obj)/efi_selftest_startimage_exit.o: $(obj)/efi_miniapp_file_image_exit.h
 
 $(obj)/efi_selftest_startimage_return.o: $(obj)/efi_miniapp_file_image_return.h
diff --git a/lib/efi_selftest/efi_selftest_block_device.c b/lib/efi_selftest/efi_selftest_block_device.c
index 1cdd830..21409ae 100644
--- a/lib/efi_selftest/efi_selftest_block_device.c
+++ b/lib/efi_selftest/efi_selftest_block_device.c
@@ -264,7 +264,7 @@
 	}
 
 	if (image) {
-		r = efi_free_pool(image);
+		r = boottime->free_pool(image);
 		if (r != EFI_SUCCESS) {
 			efi_st_error("Failed to free image\n");
 			return EFI_ST_FAILURE;
diff --git a/lib/efi_selftest/efi_selftest_hii.c b/lib/efi_selftest/efi_selftest_hii.c
index e38af7d..8a0b3bc 100644
--- a/lib/efi_selftest/efi_selftest_hii.c
+++ b/lib/efi_selftest/efi_selftest_hii.c
@@ -8,7 +8,6 @@
  */
 
 #include <efi_selftest.h>
-#include <malloc.h>
 #include "efi_selftest_hii_data.c"
 
 #define PRINT_TESTNAME efi_st_printf("%s:\n", __func__)
@@ -192,9 +191,10 @@
 			     (unsigned int)ret);
 		goto out;
 	}
-	handles = malloc(handles_size);
-	if (!handles) {
-		efi_st_error("malloc failed\n");
+	ret = boottime->allocate_pool(EFI_LOADER_DATA, handles_size,
+				      (void **)&handles);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("AllocatePool failed\n");
 		goto out;
 	}
 	ret = hii_database_protocol->list_package_lists(hii_database_protocol,
@@ -205,7 +205,11 @@
 			     (unsigned int)ret);
 		goto out;
 	}
+	ret = boottime->free_pool(handles);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("FreePool failed\n");
+		goto out;
+	}
-	free(handles);
 
 	/* STRINGS */
 	handles = NULL;
@@ -219,9 +223,10 @@
 		ret = EFI_ST_FAILURE;
 		goto out;
 	}
-	handles = malloc(handles_size);
-	if (!handles) {
-		efi_st_error("malloc failed\n");
+	ret = boottime->allocate_pool(EFI_LOADER_DATA, handles_size,
+				      (void **)&handles);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("AllocatePool failed\n");
 		ret = EFI_ST_FAILURE;
 		goto out;
 	}
@@ -234,7 +239,11 @@
 		ret = EFI_ST_FAILURE;
 		goto out;
 	}
+	ret = boottime->free_pool(handles);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("FreePool failed\n");
+		goto out;
+	}
-	free(handles);
 
 	/* GUID */
 	handles = NULL;
@@ -248,9 +257,10 @@
 		ret = EFI_ST_FAILURE;
 		goto out;
 	}
-	handles = malloc(handles_size);
-	if (!handles) {
-		efi_st_error("malloc failed\n");
+	ret = boottime->allocate_pool(EFI_LOADER_DATA, handles_size,
+				      (void **)&handles);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("AllocatePool failed\n");
 		ret = EFI_ST_FAILURE;
 		goto out;
 	}
@@ -263,7 +273,12 @@
 		ret = EFI_ST_FAILURE;
 		goto out;
 	}
-	free(handles);
+	ret = boottime->free_pool(handles);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("FreePool failed\n");
+		ret = EFI_ST_FAILURE;
+		goto out;
+	}
 
 	/* KEYBOARD_LAYOUT */
 	handles = NULL;
@@ -277,9 +292,10 @@
 		ret = EFI_ST_FAILURE;
 		goto out;
 	}
-	handles = malloc(handles_size);
-	if (!handles) {
-		efi_st_error("malloc failed\n");
+	ret = boottime->allocate_pool(EFI_LOADER_DATA, handles_size,
+				      (void **)&handles);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("AllocatePool failed\n");
 		ret = EFI_ST_FAILURE;
 		goto out;
 	}
@@ -292,7 +308,12 @@
 		ret = EFI_ST_FAILURE;
 		goto out;
 	}
-	free(handles);
+	ret = boottime->free_pool(handles);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("FreePool failed\n");
+		ret = EFI_ST_FAILURE;
+		goto out;
+	}
 
 	result = EFI_ST_SUCCESS;
 
@@ -398,9 +419,10 @@
 			     (unsigned int)ret);
 		goto out;
 	}
-	guids = malloc(guids_size);
-	if (!guids) {
-		efi_st_error("malloc failed\n");
+	ret = boottime->allocate_pool(EFI_LOADER_DATA, guids_size,
+				      (void **)&guids);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("AllocatePool failed\n");
 		goto out;
 	}
 	ret = hii_database_protocol->find_keyboard_layouts(
@@ -410,7 +432,11 @@
 			     (unsigned int)ret);
 		goto out;
 	}
+	ret = boottime->free_pool(guids);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("FreePool failed\n");
+		goto out;
+	}
-	free(guids);
 
 	result = EFI_ST_SUCCESS;
 
@@ -479,9 +505,10 @@
 			     (unsigned int)ret);
 		goto out;
 	}
-	kb_layout = malloc(kb_layout_size);
-	if (!kb_layout) {
-		efi_st_error("malloc failed\n");
+	ret = boottime->allocate_pool(EFI_LOADER_DATA, kb_layout_size,
+				      (void **)&kb_layout);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("AllocatePool failed\n");
 		goto out;
 	}
 	ret = hii_database_protocol->get_keyboard_layout(hii_database_protocol,
@@ -491,7 +518,11 @@
 			     (unsigned int)ret);
 		goto out;
 	}
+	ret = boottime->free_pool(kb_layout);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("FreePool failed\n");
+		goto out;
+	}
-	free(kb_layout);
 
 	/* current */
 	kb_layout = NULL;
@@ -738,9 +769,10 @@
 		goto out;
 	}
 	string_len += sizeof(u16);
-	string = malloc(string_len);
-	if (!string) {
-		efi_st_error("malloc failed\n");
+	ret = boottime->allocate_pool(EFI_LOADER_DATA, string_len,
+				      (void **)&string);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("AllocatePool failed\n");
 		goto out;
 	}
 	ret = hii_string_protocol->get_string(hii_string_protocol,
@@ -875,9 +907,10 @@
 			     (unsigned int)ret);
 		goto out;
 	}
-	languages = malloc(languages_len);
-	if (!languages) {
-		efi_st_error("malloc failed\n");
+	ret = boottime->allocate_pool(EFI_LOADER_DATA, languages_len,
+				      (void **)&languages);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("AllocatePool failed\n");
 		goto out;
 	}
 	ret = hii_string_protocol->get_languages(hii_string_protocol, handle,
@@ -947,9 +980,10 @@
 			     (unsigned int)ret);
 		goto out;
 	}
-	languages = malloc(languages_len);
-	if (!languages) {
-		efi_st_error("malloc failed\n");
+	ret = boottime->allocate_pool(EFI_LOADER_DATA, languages_len,
+				      (void **)&languages);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("AllocatePool failed\n");
 		goto out;
 	}
 	ret = hii_string_protocol->get_secondary_languages(hii_string_protocol,
diff --git a/lib/efi_selftest/efi_selftest_loadimage.c b/lib/efi_selftest/efi_selftest_loadimage.c
new file mode 100644
index 0000000..96faa67
--- /dev/null
+++ b/lib/efi_selftest/efi_selftest_loadimage.c
@@ -0,0 +1,529 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * efi_selftest_loadimage
+ *
+ * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * This test checks the LoadImage and StartImage boot service.
+ *
+ * The efi_selftest_miniapp_exit.efi application is loaded via a file device
+ * path and started.
+ */
+
+#include <efi_selftest.h>
+/* Include containing the efi_selftest_miniapp_exit.efi application */
+#include "efi_miniapp_file_image_exit.h"
+
+/* Block size of compressed disk image */
+#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8
+
+/* Binary logarithm of the block size */
+#define LB_BLOCK_SIZE 9
+
+#define FILE_NAME L"app.efi"
+#define VOLUME_NAME L"EfiDisk"
+
+static struct efi_boot_services *boottime;
+static efi_handle_t handle_image;
+static efi_handle_t handle_volume;
+
+static const efi_guid_t guid_device_path = DEVICE_PATH_GUID;
+static const efi_guid_t guid_simple_file_system_protocol =
+		EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
+static const efi_guid_t guid_file_info = EFI_FILE_INFO_GUID;
+static const efi_guid_t guid_file_system_info = EFI_FILE_SYSTEM_INFO_GUID;
+
+/* One 8 byte block of the compressed disk image */
+struct line {
+	size_t addr;
+	char *line;
+};
+
+/* Compressed file image */
+struct compressed_file_image {
+	size_t length;
+	struct line lines[];
+};
+
+/* File info including file name */
+struct file_info {
+	struct efi_file_info info;
+	u16 file_name[sizeof(FILE_NAME)];
+};
+
+/* File system info including volume name */
+struct file_system_info {
+	struct efi_file_system_info info;
+	u16 file_name[sizeof(VOLUME_NAME)];
+};
+
+/* Compressed file image */
+static struct compressed_file_image img = EFI_ST_DISK_IMG;
+
+/* Pointer to decompressed file image */
+static u8 *image;
+
+/* File info */
+static struct file_info priv_file_info = {
+	{
+		.size = sizeof(struct file_info),
+		.attribute = EFI_FILE_READ_ONLY,
+	},
+	FILE_NAME,
+};
+
+/* Pointer to file info */
+struct efi_file_info *file_info = &priv_file_info.info;
+
+/* Volume device path */
+static struct {
+	struct efi_device_path_vendor vendor;
+	struct efi_device_path end;
+} __packed dp_volume = {
+	.vendor = {
+		.dp = {
+			.type =	DEVICE_PATH_TYPE_HARDWARE_DEVICE,
+			.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
+			.length = sizeof(struct efi_device_path_vendor),
+		},
+		.guid = EFI_GUID(0x4f9a0ebf, 0xa179, 0x88a6, 0x25, 0x68,
+				 0x10, 0x72, 0xb1, 0x93, 0x51, 0x71),
+	},
+	.end = {
+		.type = DEVICE_PATH_TYPE_END,
+		.sub_type = DEVICE_PATH_SUB_TYPE_END,
+		.length = sizeof(struct efi_device_path),
+	}
+};
+
+/* File device path */
+static struct {
+	struct efi_device_path_vendor vendor;
+	struct efi_device_path path;
+	u16 file[sizeof(FILE_NAME)];
+	struct efi_device_path end;
+} __packed dp_file = {
+	.vendor = {
+		.dp = {
+			.type =	DEVICE_PATH_TYPE_HARDWARE_DEVICE,
+			.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
+			.length = sizeof(struct efi_device_path_vendor),
+		},
+		.guid = EFI_GUID(0x4f9a0ebf, 0xa179, 0x88a6, 0x25, 0x68,
+				 0x10, 0x72, 0xb1, 0x93, 0x51, 0x71),
+	},
+	.path = {
+		.type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
+		.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
+		.length = sizeof(struct efi_device_path) + sizeof(dp_file.file),
+	},
+	.file = FILE_NAME,
+	.end = {
+		.type = DEVICE_PATH_TYPE_END,
+		.sub_type = DEVICE_PATH_SUB_TYPE_END,
+		.length = sizeof(struct efi_device_path),
+	}
+};
+
+/* File system info */
+static struct file_system_info priv_file_system_info = {
+	{
+		.size = sizeof(struct file_system_info),
+		.read_only = true,
+		.volume_size = 0x100000,
+		.free_space = 0x0,
+		.block_size = 0x200,
+	},
+	VOLUME_NAME
+};
+
+/* Pointer to file system info */
+static struct efi_file_system_info *file_system_info =
+	&priv_file_system_info.info;
+
+/* Forward definitions of file and file system functions */
+static efi_status_t EFIAPI open_volume
+	(struct efi_simple_file_system_protocol *this,
+	 struct efi_file_handle **root);
+
+static efi_status_t EFIAPI open
+	(struct efi_file_handle *this,
+	 struct efi_file_handle **new_handle,
+	 u16 *file_name, u64 open_mode, u64 attributes);
+
+static efi_status_t EFIAPI close(struct efi_file_handle *this);
+
+static efi_status_t EFIAPI delete(struct efi_file_handle *this);
+
+static efi_status_t EFIAPI read
+	(struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer);
+
+static efi_status_t EFIAPI write
+	(struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer);
+
+static efi_status_t EFIAPI getpos(struct efi_file_handle *this, u64 *pos);
+
+static efi_status_t EFIAPI setpos(struct efi_file_handle *this, u64 pos);
+
+static efi_status_t EFIAPI getinfo
+	(struct efi_file_handle *this, const efi_guid_t *info_type,
+	 efi_uintn_t *buffer_size, void *buffer);
+
+static efi_status_t EFIAPI setinfo
+	(struct efi_file_handle *this, const efi_guid_t *info_type,
+	 efi_uintn_t buffer_size, void *buffer);
+
+static efi_status_t EFIAPI flush(struct efi_file_handle *this);
+
+/* Internal information about status of file system */
+static struct {
+	/* Difference of volume open count minus volume close count */
+	int volume_open_count;
+	/* Difference of file open count minus file close count */
+	int file_open_count;
+	/* File size */
+	u64 file_size;
+	/* Current position in file */
+	u64 file_pos;
+} priv;
+
+/* EFI_FILE_PROTOCOL for file */
+static struct efi_file_handle file = {
+	.rev = 0x00010000,
+	.open = open,
+	.close = close,
+	.delete = delete,
+	.read = read,
+	.write = write,
+	.getpos = getpos,
+	.setpos = setpos,
+	.getinfo = getinfo,
+	.setinfo = setinfo,
+	.flush = flush,
+};
+
+/* EFI_FILE_PROTOCOL for root directory */
+static struct efi_file_handle volume = {
+	.rev = 0x00010000,
+	.open = open,
+	.close = close,
+	.delete = delete,
+	.read = read,
+	.write = write,
+	.getpos = getpos,
+	.setpos = setpos,
+	.getinfo = getinfo,
+	.setinfo = setinfo,
+	.flush = flush,
+};
+
+/* EFI_SIMPLE_FILE_SYSTEM_PROTOCOL of the block device */
+struct efi_simple_file_system_protocol file_system = {
+	.rev = 0x00010000,
+	.open_volume = open_volume,
+};
+
+static efi_status_t EFIAPI open_volume
+	(struct efi_simple_file_system_protocol *this,
+	 struct efi_file_handle **root)
+{
+	if (this != &file_system || !root)
+		return EFI_INVALID_PARAMETER;
+
+	*root = &volume;
+	priv.volume_open_count++;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI open
+	(struct efi_file_handle *this,
+	 struct efi_file_handle **new_handle,
+	 u16 *file_name, u64 open_mode, u64 attributes)
+{
+	if (this != &volume)
+		return EFI_INVALID_PARAMETER;
+
+	*new_handle = &file;
+	priv.file_pos = 0;
+	priv.file_open_count++;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI close(struct efi_file_handle *this)
+{
+	if (this == &file)
+		priv.file_open_count--;
+	else if (this == &volume)
+		priv.volume_open_count--;
+	else
+		return EFI_INVALID_PARAMETER;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI delete(struct efi_file_handle *this)
+{
+	if (this != &file)
+		return EFI_INVALID_PARAMETER;
+
+	return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI read
+	(struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer)
+{
+	if (this != &file)
+		return EFI_INVALID_PARAMETER;
+
+	if (priv.file_pos >= img.length)
+		*buffer_size = 0;
+	else if (priv.file_pos + *buffer_size > img.length)
+		*buffer_size = img.length - priv.file_pos;
+
+	boottime->copy_mem(buffer, &image[priv.file_pos], *buffer_size);
+	priv.file_pos += *buffer_size;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI write
+	(struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer)
+{
+	if (this != &file)
+		return EFI_INVALID_PARAMETER;
+
+	return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI getpos(struct efi_file_handle *this, u64 *pos)
+{
+	if (this != &file)
+		return EFI_INVALID_PARAMETER;
+
+	*pos = priv.file_pos;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI setpos(struct efi_file_handle *this, u64 pos)
+{
+	if (this != &file)
+		return EFI_INVALID_PARAMETER;
+
+	priv.file_pos = pos;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI getinfo
+	(struct efi_file_handle *this, const efi_guid_t *info_type,
+	 efi_uintn_t *buffer_size, void *buffer)
+{
+	if (this == &file) {
+		if (efi_st_memcmp(info_type, &guid_file_info,
+				  sizeof(efi_guid_t)))
+			return EFI_INVALID_PARAMETER;
+		if (*buffer_size >= sizeof(struct file_info)) {
+			boottime->copy_mem(buffer, file_info,
+					   sizeof(struct file_info));
+		} else {
+			*buffer_size = sizeof(struct file_info);
+			return EFI_BUFFER_TOO_SMALL;
+		}
+	} else if (this == &volume) {
+		if (efi_st_memcmp(info_type, &guid_file_system_info,
+				  sizeof(efi_guid_t)))
+			return EFI_INVALID_PARAMETER;
+		if (*buffer_size >= sizeof(struct file_system_info)) {
+			boottime->copy_mem(buffer, file_system_info,
+					   sizeof(struct file_system_info));
+		} else {
+			*buffer_size = sizeof(struct file_system_info);
+			return EFI_BUFFER_TOO_SMALL;
+		}
+	} else {
+		return EFI_INVALID_PARAMETER;
+	}
+	return EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI setinfo
+	(struct efi_file_handle *this, const efi_guid_t *info_type,
+	 efi_uintn_t buffer_size, void *buffer)
+{
+	if (this != &file)
+		return EFI_INVALID_PARAMETER;
+
+	return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI flush(struct efi_file_handle *this)
+{
+	if (this != &file)
+		return EFI_INVALID_PARAMETER;
+
+	return EFI_UNSUPPORTED;
+}
+
+/*
+ * Decompress the disk image.
+ *
+ * @image	decompressed disk image
+ * @return	status code
+ */
+static efi_status_t decompress(u8 **image)
+{
+	u8 *buf;
+	size_t i;
+	size_t addr;
+	size_t len;
+	efi_status_t ret;
+
+	ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length,
+				      (void **)&buf);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("Out of memory\n");
+		return ret;
+	}
+	boottime->set_mem(buf, img.length, 0);
+
+	for (i = 0; ; ++i) {
+		if (!img.lines[i].line)
+			break;
+		addr = img.lines[i].addr;
+		len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE;
+		if (addr + len > img.length)
+			len = img.length - addr;
+		boottime->copy_mem(buf + addr, img.lines[i].line, len);
+	}
+	*image = buf;
+	priv.file_size = img.length;
+	file_info->file_size = img.length;
+	return ret;
+}
+
+/*
+ * Setup unit test.
+ *
+ * Decompress application image and provide a handle for the in memory block
+ * device.
+ *
+ * @handle:	handle of the loaded image
+ * @systable:	system table
+ * @return:	EFI_ST_SUCCESS for success
+ */
+static int setup(const efi_handle_t handle,
+		 const struct efi_system_table *systable)
+{
+	efi_status_t ret;
+
+	handle_image = handle;
+	boottime = systable->boottime;
+
+	/* Load the application image into memory */
+	decompress(&image);
+
+	ret = boottime->install_protocol_interface
+		(&handle_volume, &guid_device_path, EFI_NATIVE_INTERFACE,
+		 &dp_volume);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("Failed to install device path\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->install_protocol_interface
+		(&handle_volume, &guid_simple_file_system_protocol,
+		 EFI_NATIVE_INTERFACE, &file_system);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("Failed to install simple file system protocol\n");
+		return EFI_ST_FAILURE;
+	}
+
+	return EFI_ST_SUCCESS;
+}
+
+/*
+ * Tear down unit test.
+ *
+ * Uninstall protocols and free memory.
+ *
+ * @return:	EFI_ST_SUCCESS for success
+ */
+static int teardown(void)
+{
+	efi_status_t ret = EFI_ST_SUCCESS;
+
+	if (handle_volume) {
+		ret = boottime->uninstall_protocol_interface
+			(handle_volume, &guid_simple_file_system_protocol,
+			 &file_system);
+		if (ret != EFI_SUCCESS) {
+			efi_st_error
+				("Failed to uninstall simple file system protocol\n");
+			return EFI_ST_FAILURE;
+		}
+		ret = boottime->uninstall_protocol_interface
+			(handle_volume, &guid_device_path, &dp_volume);
+		if (ret != EFI_SUCCESS) {
+			efi_st_error
+				("Failed to uninstall device path protocol\n");
+			return EFI_ST_FAILURE;
+		}
+	}
+
+	if (image) {
+		ret = boottime->free_pool(image);
+		if (ret != EFI_SUCCESS) {
+			efi_st_error("Failed to free image\n");
+			return EFI_ST_FAILURE;
+		}
+	}
+	return ret;
+}
+
+/*
+ * Execute unit test.
+ *
+ * Load and start the application image.
+ *
+ * @return:	EFI_ST_SUCCESS for success
+ */
+static int execute(void)
+{
+	efi_status_t ret;
+	efi_handle_t handle;
+
+	ret = boottime->load_image(false, handle_image, &dp_file.vendor.dp,
+				   NULL, 0, &handle);
+	if (ret != EFI_SUCCESS) {
+		efi_st_error("Failed to load image\n");
+		return EFI_ST_FAILURE;
+	}
+	ret = boottime->start_image(handle, NULL, NULL);
+	if (ret != EFI_UNSUPPORTED) {
+		efi_st_error("Wrong return value from application\n");
+		return EFI_ST_FAILURE;
+	}
+
+	if (priv.file_open_count) {
+		efi_st_error("File open count = %d, expected 0\n",
+			     priv.file_open_count);
+		return EFI_ST_FAILURE;
+	}
+	if (priv.volume_open_count) {
+		efi_st_error("Volume open count = %d, expected 0\n",
+			     priv.volume_open_count);
+		return EFI_ST_FAILURE;
+	}
+
+	return EFI_ST_SUCCESS;
+}
+
+EFI_UNIT_TEST(loadimage) = {
+	.name = "load image from file",
+	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
+	.setup = setup,
+	.execute = execute,
+	.teardown = teardown,
+};
diff --git a/lib/efi_selftest/efi_selftest_startimage_exit.c b/lib/efi_selftest/efi_selftest_startimage_exit.c
index 0d9e164..fa4b7d4 100644
--- a/lib/efi_selftest/efi_selftest_startimage_exit.c
+++ b/lib/efi_selftest/efi_selftest_startimage_exit.c
@@ -103,7 +103,7 @@
 	efi_status_t r = EFI_ST_SUCCESS;
 
 	if (image) {
-		r = efi_free_pool(image);
+		r = boottime->free_pool(image);
 		if (r != EFI_SUCCESS) {
 			efi_st_error("Failed to free image\n");
 			return EFI_ST_FAILURE;
diff --git a/lib/efi_selftest/efi_selftest_startimage_return.c b/lib/efi_selftest/efi_selftest_startimage_return.c
index 3c6249f..fabf53d 100644
--- a/lib/efi_selftest/efi_selftest_startimage_return.c
+++ b/lib/efi_selftest/efi_selftest_startimage_return.c
@@ -103,7 +103,7 @@
 	efi_status_t r = EFI_ST_SUCCESS;
 
 	if (image) {
-		r = efi_free_pool(image);
+		r = boottime->free_pool(image);
 		if (r != EFI_SUCCESS) {
 			efi_st_error("Failed to free image\n");
 			return EFI_ST_FAILURE;
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index de5db1a..1b6c154 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -288,6 +288,8 @@
 	for (i = 0; i < len && buf + utf16_utf8_strnlen(str, 1) <= end; ++i) {
 		s32 s = utf16_get(&str);
 
+		if (s < 0)
+			s = '?';
 		utf8_put(s, &buf);
 	}
 	for (; len < field_width; --field_width)
diff --git a/test/unicode_ut.c b/test/unicode_ut.c
index 84fc9a3..8e1efe6 100644
--- a/test/unicode_ut.c
+++ b/test/unicode_ut.c
@@ -50,7 +50,7 @@
 static const char j2[] = {0x6a, 0x32, 0xc3, 0xc3, 0x6c, 0x00};
 static const char j3[] = {0x6a, 0x33, 0xf0, 0x90, 0xf0, 0x00};
 
-static int ut_u16_strdup(struct unit_test_state *uts)
+static int unicode_test_u16_strdup(struct unit_test_state *uts)
 {
 	u16 *copy = u16_strdup(c4);
 
@@ -59,9 +59,9 @@
 	free(copy);
 	return 0;
 }
-UNICODE_TEST(ut_u16_strdup);
+UNICODE_TEST(unicode_test_u16_strdup);
 
-static int ut_u16_strcpy(struct unit_test_state *uts)
+static int unicode_test_u16_strcpy(struct unit_test_state *uts)
 {
 	u16 *r;
 	u16 copy[10];
@@ -71,11 +71,11 @@
 	ut_assert(!memcmp(copy, c1, sizeof(c1)));
 	return 0;
 }
-UNICODE_TEST(ut_u16_strcpy);
+UNICODE_TEST(unicode_test_u16_strcpy);
 
 /* U-Boot uses UTF-16 strings in the EFI context only. */
 #if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
-static int ut_string16(struct unit_test_state *uts)
+static int unicode_test_string16(struct unit_test_state *uts)
 {
 	char buf[20];
 
@@ -113,10 +113,10 @@
 
 	return 0;
 }
-UNICODE_TEST(ut_string16);
+UNICODE_TEST(unicode_test_string16);
 #endif
 
-static int ut_utf8_get(struct unit_test_state *uts)
+static int unicode_test_utf8_get(struct unit_test_state *uts)
 {
 	const char *s;
 	s32 code;
@@ -152,9 +152,9 @@
 
 	return 0;
 }
-UNICODE_TEST(ut_utf8_get);
+UNICODE_TEST(unicode_test_utf8_get);
 
-static int ut_utf8_put(struct unit_test_state *uts)
+static int unicode_test_utf8_put(struct unit_test_state *uts)
 {
 	char buffer[8] = { 0, };
 	char *pos;
@@ -190,9 +190,9 @@
 
 	return 0;
 }
-UNICODE_TEST(ut_utf8_put);
+UNICODE_TEST(unicode_test_utf8_put);
 
-static int ut_utf8_utf16_strlen(struct unit_test_state *uts)
+static int unicode_test_utf8_utf16_strlen(struct unit_test_state *uts)
 {
 	ut_asserteq(6, utf8_utf16_strlen(d1));
 	ut_asserteq(8, utf8_utf16_strlen(d2));
@@ -206,9 +206,9 @@
 
 	return 0;
 }
-UNICODE_TEST(ut_utf8_utf16_strlen);
+UNICODE_TEST(unicode_test_utf8_utf16_strlen);
 
-static int ut_utf8_utf16_strnlen(struct unit_test_state *uts)
+static int unicode_test_utf8_utf16_strnlen(struct unit_test_state *uts)
 {
 	ut_asserteq(3, utf8_utf16_strnlen(d1, 3));
 	ut_asserteq(6, utf8_utf16_strnlen(d1, 13));
@@ -224,7 +224,7 @@
 
 	return 0;
 }
-UNICODE_TEST(ut_utf8_utf16_strnlen);
+UNICODE_TEST(unicode_test_utf8_utf16_strnlen);
 
 /**
  * ut_u16_strcmp() - Compare to u16 strings.
@@ -234,7 +234,7 @@
  * @count:	number of u16 to compare
  * Return:	-1 if a1 < a2, 0 if a1 == a2, 1 if a1 > a2
  */
-static int ut_u16_strcmp(const u16 *a1, const u16 *a2, size_t count)
+static int unicode_test_u16_strcmp(const u16 *a1, const u16 *a2, size_t count)
 {
 	for (; (*a1 || *a2) && count; ++a1, ++a2, --count) {
 		if (*a1 < *a2)
@@ -245,7 +245,7 @@
 	return 0;
 }
 
-static int ut_utf8_utf16_strcpy(struct unit_test_state *uts)
+static int unicode_test_utf8_utf16_strcpy(struct unit_test_state *uts)
 {
 	u16 buf[16];
 	u16 *pos;
@@ -253,44 +253,44 @@
 	pos = buf;
 	utf8_utf16_strcpy(&pos, d1);
 	ut_asserteq(6, pos - buf);
-	ut_assert(!ut_u16_strcmp(buf, c1, SIZE_MAX));
+	ut_assert(!unicode_test_u16_strcmp(buf, c1, SIZE_MAX));
 
 	pos = buf;
 	utf8_utf16_strcpy(&pos, d2);
 	ut_asserteq(8, pos - buf);
-	ut_assert(!ut_u16_strcmp(buf, c2, SIZE_MAX));
+	ut_assert(!unicode_test_u16_strcmp(buf, c2, SIZE_MAX));
 
 	pos = buf;
 	utf8_utf16_strcpy(&pos, d3);
 	ut_asserteq(3, pos - buf);
-	ut_assert(!ut_u16_strcmp(buf, c3, SIZE_MAX));
+	ut_assert(!unicode_test_u16_strcmp(buf, c3, SIZE_MAX));
 
 	pos = buf;
 	utf8_utf16_strcpy(&pos, d4);
 	ut_asserteq(6, pos - buf);
-	ut_assert(!ut_u16_strcmp(buf, c4, SIZE_MAX));
+	ut_assert(!unicode_test_u16_strcmp(buf, c4, SIZE_MAX));
 
 	/* Illegal utf-8 strings */
 	pos = buf;
 	utf8_utf16_strcpy(&pos, j1);
 	ut_asserteq(4, pos - buf);
-	ut_assert(!ut_u16_strcmp(buf, L"j1?l", SIZE_MAX));
+	ut_assert(!unicode_test_u16_strcmp(buf, L"j1?l", SIZE_MAX));
 
 	pos = buf;
 	utf8_utf16_strcpy(&pos, j2);
 	ut_asserteq(4, pos - buf);
-	ut_assert(!ut_u16_strcmp(buf, L"j2?l", SIZE_MAX));
+	ut_assert(!unicode_test_u16_strcmp(buf, L"j2?l", SIZE_MAX));
 
 	pos = buf;
 	utf8_utf16_strcpy(&pos, j3);
 	ut_asserteq(3, pos - buf);
-	ut_assert(!ut_u16_strcmp(buf, L"j3?", SIZE_MAX));
+	ut_assert(!unicode_test_u16_strcmp(buf, L"j3?", SIZE_MAX));
 
 	return 0;
 }
-UNICODE_TEST(ut_utf8_utf16_strcpy);
+UNICODE_TEST(unicode_test_utf8_utf16_strcpy);
 
-int ut_utf8_utf16_strncpy(struct unit_test_state *uts)
+static int unicode_test_utf8_utf16_strncpy(struct unit_test_state *uts)
 {
 	u16 buf[16];
 	u16 *pos;
@@ -300,41 +300,41 @@
 	utf8_utf16_strncpy(&pos, d1, 4);
 	ut_asserteq(4, pos - buf);
 	ut_assert(!buf[4]);
-	ut_assert(!ut_u16_strcmp(buf, c1, 4));
+	ut_assert(!unicode_test_u16_strcmp(buf, c1, 4));
 
 	pos = buf;
 	memset(buf, 0, sizeof(buf));
 	utf8_utf16_strncpy(&pos, d2, 10);
 	ut_asserteq(8, pos - buf);
 	ut_assert(buf[4]);
-	ut_assert(!ut_u16_strcmp(buf, c2, SIZE_MAX));
+	ut_assert(!unicode_test_u16_strcmp(buf, c2, SIZE_MAX));
 
 	pos = buf;
 	memset(buf, 0, sizeof(buf));
 	utf8_utf16_strncpy(&pos, d3, 2);
 	ut_asserteq(2, pos - buf);
 	ut_assert(!buf[2]);
-	ut_assert(!ut_u16_strcmp(buf, c3, 2));
+	ut_assert(!unicode_test_u16_strcmp(buf, c3, 2));
 
 	pos = buf;
 	memset(buf, 0, sizeof(buf));
 	utf8_utf16_strncpy(&pos, d4, 2);
 	ut_asserteq(4, pos - buf);
 	ut_assert(!buf[4]);
-	ut_assert(!ut_u16_strcmp(buf, c4, 4));
+	ut_assert(!unicode_test_u16_strcmp(buf, c4, 4));
 
 	pos = buf;
 	memset(buf, 0, sizeof(buf));
 	utf8_utf16_strncpy(&pos, d4, 10);
 	ut_asserteq(6, pos - buf);
 	ut_assert(buf[5]);
-	ut_assert(!ut_u16_strcmp(buf, c4, SIZE_MAX));
+	ut_assert(!unicode_test_u16_strcmp(buf, c4, SIZE_MAX));
 
 	return 0;
 }
-UNICODE_TEST(ut_utf8_utf16_strncpy);
+UNICODE_TEST(unicode_test_utf8_utf16_strncpy);
 
-static int ut_utf16_get(struct unit_test_state *uts)
+static int unicode_test_utf16_get(struct unit_test_state *uts)
 {
 	const u16 *s;
 	s32 code;
@@ -358,9 +358,9 @@
 
 	return 0;
 }
-UNICODE_TEST(ut_utf16_get);
+UNICODE_TEST(unicode_test_utf16_get);
 
-static int ut_utf16_put(struct unit_test_state *uts)
+static int unicode_test_utf16_put(struct unit_test_state *uts)
 {
 	u16 buffer[4] = { 0, };
 	u16 *pos;
@@ -386,9 +386,9 @@
 
 	return 0;
 }
-UNICODE_TEST(ut_utf16_put);
+UNICODE_TEST(unicode_test_utf16_put);
 
-int ut_utf16_strnlen(struct unit_test_state *uts)
+static int unicode_test_utf16_strnlen(struct unit_test_state *uts)
 {
 	ut_asserteq(3, utf16_strnlen(c1, 3));
 	ut_asserteq(6, utf16_strnlen(c1, 13));
@@ -404,9 +404,9 @@
 
 	return 0;
 }
-UNICODE_TEST(ut_utf16_strnlen);
+UNICODE_TEST(unicode_test_utf16_strnlen);
 
-int ut_utf16_utf8_strlen(struct unit_test_state *uts)
+static int unicode_test_utf16_utf8_strlen(struct unit_test_state *uts)
 {
 	ut_asserteq(6, utf16_utf8_strlen(c1));
 	ut_asserteq(9, utf16_utf8_strlen(c2));
@@ -420,9 +420,9 @@
 
 	return 0;
 }
-UNICODE_TEST(ut_utf16_utf8_strlen);
+UNICODE_TEST(unicode_test_utf16_utf8_strlen);
 
-int ut_utf16_utf8_strnlen(struct unit_test_state *uts)
+static int unicode_test_utf16_utf8_strnlen(struct unit_test_state *uts)
 {
 	ut_asserteq(3, utf16_utf8_strnlen(c1, 3));
 	ut_asserteq(6, utf16_utf8_strnlen(c1, 13));
@@ -432,9 +432,9 @@
 	ut_asserteq(12, utf16_utf8_strnlen(c4, 3));
 	return 0;
 }
-UNICODE_TEST(ut_utf16_utf8_strnlen);
+UNICODE_TEST(unicode_test_utf16_utf8_strnlen);
 
-int ut_utf16_utf8_strcpy(struct unit_test_state *uts)
+static int unicode_test_utf16_utf8_strcpy(struct unit_test_state *uts)
 {
 	char buf[16];
 	char *pos;
@@ -477,9 +477,9 @@
 
 	return 0;
 }
-UNICODE_TEST(ut_utf16_utf8_strcpy);
+UNICODE_TEST(unicode_test_utf16_utf8_strcpy);
 
-int ut_utf16_utf8_strncpy(struct unit_test_state *uts)
+static int unicode_test_utf16_utf8_strncpy(struct unit_test_state *uts)
 {
 	char buf[16];
 	char *pos;
@@ -521,9 +521,9 @@
 
 	return 0;
 }
-UNICODE_TEST(ut_utf16_utf8_strncpy);
+UNICODE_TEST(unicode_test_utf16_utf8_strncpy);
 
-int ut_utf_to_lower(struct unit_test_state *uts)
+static int unicode_test_utf_to_lower(struct unit_test_state *uts)
 {
 	ut_asserteq('@', utf_to_lower('@'));
 	ut_asserteq('a', utf_to_lower('A'));
@@ -538,9 +538,9 @@
 #endif
 	return 0;
 }
-UNICODE_TEST(ut_utf_to_lower);
+UNICODE_TEST(unicode_test_utf_to_lower);
 
-int ut_utf_to_upper(struct unit_test_state *uts)
+static int unicode_test_utf_to_upper(struct unit_test_state *uts)
 {
 	ut_asserteq('`', utf_to_upper('`'));
 	ut_asserteq('A', utf_to_upper('a'));
@@ -555,7 +555,7 @@
 #endif
 	return 0;
 }
-UNICODE_TEST(ut_utf_to_upper);
+UNICODE_TEST(unicode_test_utf_to_upper);
 
 int do_ut_unicode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {