For use by the EFI LoadImage boot service, implement support of opening
a file within an EFI simple file system by its barebox-generated EFI
device path.

Signed-off-by: Ahmad Fatoum <[email protected]>
---
 efi/loader/protocols/file.c | 67 +++++++++++++++++++++++++++++++++++++
 include/efi/loader/file.h   |  3 ++
 2 files changed, 70 insertions(+)

diff --git a/efi/loader/protocols/file.c b/efi/loader/protocols/file.c
index 3858cd3e1682..03d2dc3a0d38 100644
--- a/efi/loader/protocols/file.c
+++ b/efi/loader/protocols/file.c
@@ -922,6 +922,73 @@ static const struct efi_file_handle 
efi_file_handle_protocol = {
        .flush_ex = efi_file_flush_ex,
 };
 
+/**
+ * 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;
+       struct efi_file_handle *f;
+       efi_status_t efiret;
+
+       v = efi_fs_from_path(fp);
+       if (!v)
+               return NULL;
+
+       efiret = v->open_volume(v, &f);
+       if (efiret != 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.
+        */
+       while (fp) {
+               struct efi_device_path_file_path *fdp =
+                       container_of(fp, struct efi_device_path_file_path, 
header);
+               struct efi_file_handle *f2;
+               efi_char16_t *filename;
+               size_t filename_sz;
+
+               if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
+                       pr_warn("bad file path!\n");
+                       f->close(f);
+                       return NULL;
+               }
+
+               /*
+                * UEFI specification requires pointers that are passed to
+                * protocol member functions to be aligned.  So memcpy it
+                * unconditionally
+                */
+               if (fdp->header.length <= offsetof(struct 
efi_device_path_file_path, path_name))
+                       return NULL;
+               filename_sz = fdp->header.length -
+                       offsetof(struct efi_device_path_file_path, path_name);
+               filename = memdup(fdp->path_name, filename_sz);
+               if (!filename)
+                       return NULL;
+               efiret = f->open(f, &f2, filename, EFI_FILE_MODE_READ, 0);
+               free(filename);
+               if (efiret != EFI_SUCCESS)
+                       return NULL;
+
+               fp = efi_dp_next(fp);
+
+               f->close(f);
+               f = f2;
+       }
+
+       return f;
+}
+
 static efi_status_t EFIAPI
 efi_open_volume(struct efi_simple_file_system_protocol *this,
                struct efi_file_handle **root)
diff --git a/include/efi/loader/file.h b/include/efi/loader/file.h
index ce52cc5df8aa..a4f071f97817 100644
--- a/include/efi/loader/file.h
+++ b/include/efi/loader/file.h
@@ -12,6 +12,9 @@ struct efi_file_handle;
 struct efi_simple_file_system_protocol *
 efi_fs_from_path(struct efi_device_path *fp);
 
+/* open file from device-path: */
+struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp);
+
 efi_status_t efi_file_size(struct efi_file_handle *fh, efi_uintn_t *size);
 
 /**
-- 
2.47.3


Reply via email to