[PATCH] efi_loader: fix a problem in loading an image from a short-path

AKASHI Takahiro takahiro.akashi at linaro.org
Thu Apr 28 06:50:12 CEST 2022


Booting from a short-form device path which starts with the first element
being a File Path Media Device Path failed because it doesn't contain
any valid device with simple file system protocol and efi_dp_find_obj()
in efi_load_image_from_path() will return NULL.
For instance,
/VenHw(e61d73b9-a384-4acc-aeab-82e828f3628b)/Scsi(0,0)/\helloworld.efi
  -> shortened version: /\helloworld.efi

With this patch applied, all the media devices with simple file system
protocol are enumerated and the boot manager attempts to boot temporarily
generated device paths one-by-one.

This new implementation is still a bit incompatible with the UEFI
specification in terms of:
* not creating real boot options
* not distinguishing removable media and fix media
(See section 3.1.3 "Boot Options".)

But it still gives us a closer and better solution than the current.

Fixes: commit 9cdf470274ff ("efi_loader: support booting via short-form device-path")
Signed-off-by: AKASHI Takahiro <takahiro.akashi at linaro.org>
---
 include/efi_loader.h          |  3 ++
 lib/efi_loader/efi_boottime.c | 87 +++++++++++++++++++++++++++++++----
 lib/efi_loader/efi_file.c     | 35 ++++++++++----
 3 files changed, 107 insertions(+), 18 deletions(-)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index ba79a9afb404..9730c1375a55 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -661,6 +661,9 @@ void efi_signal_event(struct efi_event *event);
 struct efi_simple_file_system_protocol *efi_simple_file_system(
 		struct blk_desc *desc, int part, struct efi_device_path *dp);
 
+/* open file from simple file system */
+struct efi_file_handle *efi_file_from_fs(struct efi_simple_file_system_protocol *v,
+					 struct efi_device_path *fp);
 /* open file from device-path: */
 struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp);
 
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 5bcb8253edba..39b0e8f7ade0 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -1868,19 +1868,21 @@ out:
 }
 
 /**
- * efi_load_image_from_file() - load an image from file system
+ * __efi_load_image_from_file() - load an image from file system
  *
  * Read a file into a buffer allocated as EFI_BOOT_SERVICES_DATA. It is the
  * callers obligation to update the memory type as needed.
  *
+ * @v:			simple file system
  * @file_path:		the path of the image to load
  * @buffer:		buffer containing the loaded image
  * @size:		size of the loaded image
  * Return:		status code
  */
 static
-efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
-				      void **buffer, efi_uintn_t *size)
+efi_status_t __efi_load_image_from_file(struct efi_simple_file_system_protocol *v,
+					struct efi_device_path *file_path,
+					void **buffer, efi_uintn_t *size)
 {
 	struct efi_file_handle *f;
 	efi_status_t ret;
@@ -1888,7 +1890,11 @@ efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
 	efi_uintn_t bs;
 
 	/* Open file */
-	f = efi_file_from_path(file_path);
+	if (v)
+		f = efi_file_from_fs(v, file_path);
+	else
+		/* file_path should have a device path */
+		f = efi_file_from_path(file_path);
 	if (!f)
 		return EFI_NOT_FOUND;
 
@@ -1921,6 +1927,64 @@ error:
 	return ret;
 }
 
+/**
+ * efi_load_image_from_file() - load an image from file system
+ *
+ * Read a file into a buffer allocated as EFI_BOOT_SERVICES_DATA. It is the
+ * callers obligation to update the memory type as needed.
+ *
+ * @file_path:		the path of the image to load
+ * @buffer:		buffer containing the loaded image
+ * @size:		size of the loaded image
+ * Return:		status code
+ */
+static
+efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
+				      void **buffer, efi_uintn_t *size)
+{
+	efi_uintn_t no_handles;
+	efi_handle_t *handles;
+	struct efi_handler *handler;
+	struct efi_simple_file_system_protocol *fs;
+	int i;
+	efi_status_t ret;
+
+	/* if a file_path contains a device path */
+	if (!EFI_DP_TYPE(file_path, MEDIA_DEVICE, FILE_PATH))
+		return __efi_load_image_from_file(NULL, file_path, buffer, size);
+
+	/* no explicit device specified */
+	ret = EFI_CALL(efi_locate_handle_buffer(
+					BY_PROTOCOL,
+					&efi_simple_file_system_protocol_guid,
+					NULL,
+					&no_handles,
+					&handles));
+	if (ret != EFI_SUCCESS)
+		return ret;
+	if (!no_handles)
+		return EFI_NOT_FOUND;
+
+	for (i = 0; i < no_handles; i++) {
+		ret = efi_search_protocol(handles[i],
+					  &efi_simple_file_system_protocol_guid,
+					  &handler);
+		if (ret != EFI_SUCCESS)
+			/* unlikely */
+			continue;
+
+		fs = handler->protocol_interface;
+		if (!fs)
+			continue;
+
+		ret = __efi_load_image_from_file(fs, file_path, buffer, size);
+		if (ret == EFI_SUCCESS)
+			return ret;
+	}
+
+	return EFI_NOT_FOUND;
+}
+
 /**
  * efi_load_image_from_path() - load an image using a file path
  *
@@ -1940,7 +2004,7 @@ efi_status_t efi_load_image_from_path(bool boot_policy,
 {
 	efi_handle_t device;
 	efi_status_t ret;
-	struct efi_device_path *dp, *rem;
+	struct efi_device_path *rem;
 	struct efi_load_file_protocol *load_file_protocol = NULL;
 	efi_uintn_t buffer_size;
 	uint64_t addr, pages;
@@ -1950,12 +2014,15 @@ efi_status_t efi_load_image_from_path(bool boot_policy,
 	*buffer = NULL;
 	*size = 0;
 
-	dp = file_path;
-	device = efi_dp_find_obj(dp, NULL, &rem);
-	ret = efi_search_protocol(device, &efi_simple_file_system_protocol_guid,
-				  NULL);
+	/* try first for simple file system protocols */
+	ret = efi_load_image_from_file(file_path, buffer, size);
 	if (ret == EFI_SUCCESS)
-		return efi_load_image_from_file(file_path, buffer, size);
+		return ret;
+
+	/* TODO: does this really make sense? */
+	device = efi_dp_find_obj(file_path, NULL, &rem);
+	if (!device)
+		return EFI_NOT_FOUND;
 
 	ret = efi_search_protocol(device, &efi_guid_load_file_protocol, NULL);
 	if (ret == EFI_SUCCESS) {
diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c
index 7a7077e6d032..2d6a432b168b 100644
--- a/lib/efi_loader/efi_file.c
+++ b/lib/efi_loader/efi_file.c
@@ -1083,16 +1083,16 @@ static const struct efi_file_handle efi_file_handle_protocol = {
 /**
  * efi_file_from_path() - open file via device path
  *
- * @fp:		device path
+ * @v:		simple file system
+ * @fp:		file 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_file_handle *efi_file_from_fs(struct efi_simple_file_system_protocol *v,
+					 struct efi_device_path *fp)
 {
-	struct efi_simple_file_system_protocol *v;
 	struct efi_file_handle *f;
 	efi_status_t ret;
 
-	v = efi_fs_from_path(fp);
 	if (!v)
 		return NULL;
 
@@ -1100,10 +1100,6 @@ struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
 	if (ret != EFI_SUCCESS)
 		return NULL;
 
-	/* 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.
@@ -1138,6 +1134,29 @@ struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
 	return f;
 }
 
+/**
+ * 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;
+
+	v = efi_fs_from_path(fp);
+	if (!v)
+		return NULL;
+
+	/* Skip over device-path nodes before the file path. */
+	while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
+		fp = efi_dp_next(fp);
+	if (!fp)
+		return NULL;
+
+	return efi_file_from_fs(v, fp);
+}
+
 static efi_status_t EFIAPI
 efi_open_volume(struct efi_simple_file_system_protocol *this,
 		struct efi_file_handle **root)
-- 
2.33.0



More information about the U-Boot mailing list