[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