On 12/4/21 10:43, Heinrich Schuchardt wrote:
On 12/4/21 16:56, Simon Glass wrote:
When starting the app, locate all block devices and make them available
to U-Boot. This allows listing partitions and accessing files in
filesystems.

EFI also has the concept of 'disks', meaning boot media. For now, this
is not obviously useful in U-Boot, but add code to at least locate these.
This can be expanded later as needed.

Series-changes; 2
- Store device path in struct efi_media_plat
- Don't export efi_bind_block()
- Only bind devices for media devices, not for partitions
- Show devices that are processed
- Update documentation

Signed-off-by: Simon Glass <s...@chromium.org>
---

(no changes since v1)

  doc/develop/uefi/u-boot_on_efi.rst |   4 +-
  include/efi.h                      |   6 +-
  include/efi_api.h                  |  15 ++
  lib/efi/efi_app.c                  | 223 +++++++++++++++++++++++++++++
  4 files changed, 243 insertions(+), 5 deletions(-)

diff --git a/doc/develop/uefi/u-boot_on_efi.rst
b/doc/develop/uefi/u-boot_on_efi.rst
index 5f2f850f071..8f81b799072 100644
--- a/doc/develop/uefi/u-boot_on_efi.rst
+++ b/doc/develop/uefi/u-boot_on_efi.rst
@@ -265,9 +265,7 @@ This work could be extended in a number of ways:

  - Figure out how to solve the interrupt problem

-- Add more drivers to the application side (e.g. block devices, USB,
-  environment access). This would mostly be an academic exercise as a
strong
-  use case is not readily apparent, but it might be fun.
+- Add more drivers to the application side (e.g.USB, environment
access).

  - Avoid turning off boot services in the stub. Instead allow U-Boot
to make
    use of boot services in case it wants to. It is unclear what it
might want
diff --git a/include/efi.h b/include/efi.h
index 0ec5913ddd1..908c5dc6ebd 100644
--- a/include/efi.h
+++ b/include/efi.h
@@ -419,10 +419,12 @@ struct efi_priv {
   *
   * @handle: handle of the controller on which this driver is installed
   * @blkio: block io protocol proxied by this driver
+ * @device_path: EFI path to the device
   */
  struct efi_media_plat {
-    efi_handle_t        handle;
-    struct efi_block_io    *blkio;
+    efi_handle_t handle;
+    struct efi_block_io *blkio;
+    struct efi_device_path *device_path;
  };

  /* Base address of the EFI image */
diff --git a/include/efi_api.h b/include/efi_api.h
index 80109f012bc..ec9fa89a935 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -2035,4 +2035,19 @@ struct efi_firmware_management_protocol {
              const u16 *package_version_name);
  };

+#define EFI_DISK_IO_PROTOCOL_GUID    \
+    EFI_GUID(0xce345171, 0xba0b, 0x11d2, 0x8e, 0x4f, \
+         0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
+
+struct efi_disk {
+    u64 revision;
+    efi_status_t (EFIAPI *read_disk)(struct efi_disk *this, u32
media_id,
+                     u64 offset, efi_uintn_t buffer_size,
+                     void *buffer);
+
+    efi_status_t (EFIAPI *write_disk)(struct efi_disk *this, u32
media_id,
+                      u64 offset, efi_uintn_t buffer_size,
+                      void *buffer);
+};
+
  #endif
diff --git a/lib/efi/efi_app.c b/lib/efi/efi_app.c
index f61665686c5..e38d46b15db 100644
--- a/lib/efi/efi_app.c
+++ b/lib/efi/efi_app.c
@@ -21,6 +21,9 @@
  #include <efi.h>
  #include <efi_api.h>
  #include <sysreset.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/root.h>

  DECLARE_GLOBAL_DATA_PTR;

@@ -46,6 +49,64 @@ int efi_info_get(enum efi_entry_t type, void
**datap, int *sizep)
      return -ENOSYS;
  }

+/**
+ * efi_print_str() - Print a UFT-16 string to the U-Boot console
+ *
+ * @str: String to print
+ */
+static void efi_print_str(const u16 *str)
+{
+    while (*str) {
+        int ch = *str++;
+
+        if (ch > ' ' && ch < 127)
+            putc(ch);
+    }
+}
+
+/**
+ * efi_bind_block() - bind a new block device to an EFI device
+ *
+ * Binds a new top-level EFI_MEDIA device as well as a child block
device so
+ * that the block device can be accessed in U-Boot.
+ *
+ * The device can then be accessed using 'part list efi 0', 'fat ls
efi 0:1',
+ * for example, just like any other interface type.
+ *
+ * @handle: handle of the controller on which this driver is installed
+ * @blkio: block io protocol proxied by this driver
+ * @device_path: EFI device path structure for this
+ * @len: Length of @device_path in bytes
+ * @devp: Returns the bound device
+ * @return 0 if OK, -ve on error
+ */
+int efi_bind_block(efi_handle_t handle, struct efi_block_io *blkio,
+           struct efi_device_path *device_path, int len,
+           struct udevice **devp)
+{
+    struct efi_media_plat plat;
+    struct udevice *dev;
+    char name[18];
+    int ret;
+
+    plat.handle = handle;
+    plat.blkio = blkio;
+    plat.device_path = malloc(device_path->length);
+    if (!plat.device_path)
+        return log_msg_ret("path", -ENOMEM);
+    memcpy(plat.device_path, device_path, device_path->length);
+    ret = device_bind(dm_root(), DM_DRIVER_GET(efi_media), "efi_media",
+              &plat, ofnode_null(), &dev);
+    if (ret)
+        return log_msg_ret("bind", ret);
+
+    snprintf(name, sizeof(name), "efi_media_%x", dev_seq(dev));
+    device_set_name(dev, name);
+    *devp = dev;
+
+    return 0;
+}
+
  static efi_status_t setup_memory(struct efi_priv *priv)
  {
      struct efi_boot_services *boot = priv->boot;
@@ -105,6 +166,168 @@ static void free_memory(struct efi_priv *priv)
      global_data_ptr = NULL;
  }



Missing function description


+static int setup_disks(void)
+{
+    /* This is not fully implemented yet */

see hint below.

+    return 0;
+
+    efi_guid_t efi_disk_guid = EFI_DISK_IO_PROTOCOL_GUID;

U-Boot does not implement the EFI_DISK_IO_PROTOCOL. So you will not be
able to run U-Boot as an EFI app loaded by U-Boot which might be nice
for testing.

The more basic protocol is the EFI_BLOCK_IO_PROTOCOL. The difference is
that the EFI_BLOCK_IO_PROTOCOL requires using a properly aligned buffer.

+    struct efi_boot_services *boot = efi_get_boot();
+    struct efi_disk *disk;
+    int ret;
+
+    if (!boot)
+        return log_msg_ret("sys", -ENOSYS);
+    ret = boot->locate_protocol(&efi_disk_guid, NULL, (void **)&disk);

If you want to find all handles implementing a protocol, you can use
EFI_BOOT_SERVICES.LocateHandleBuffer() with SearchType ByProtocol.

I guess we should add this patch to U-Boot once it is completed.

Best regards

Heinrich

+    if (ret)
+        return log_msg_ret("prot", -ENOTSUPP);
+
+    return 0;
+}
+
+/**
+ * devpath_is_partition() - Figure out if a device path is a partition
+ *
+ * Checks if a device path refers to a partition on some media
device. This
+ * works by checking for a valid partition number in a hard-driver
media device
+ * as the final component of the device path.
+ *
+ * @return true if a partition, false if not (e.g. it might be media
which
+ *    contains partitions)

Please, stick to the format for function descriptions defined in
https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-documentation


The description for parameter @path: is missing.
@return does not exist. Please, use Return:.


+ */
+static bool devpath_is_partition(const struct efi_device_path *path)
+{
+    const struct efi_device_path *p;
+    bool was_part;
+
+    for (p = path; p->type != DEVICE_PATH_TYPE_END;
+         p = (void *)p + p->length) {
+        was_part = false;
+        if (p->type == DEVICE_PATH_TYPE_MEDIA_DEVICE &&
+            p->sub_type == DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH) {
+            struct efi_device_path_hard_drive_path *hd =
+                (void *)path;
+
+            if (hd->partition_number)
+                was_part = true;
+        }
+    }
+
+    return was_part;
+}
+
+/**
+ * setup_block() - Find all block devices and setup EFI devices for them
+ *
+ * Partitions are ignored, since U-Boot has partition handling.
Errors with
+ * particular devices produce a warning but execution continues to
try to
+ * find others.
+ *
+ * @return 0 if found, -ENOSYS if there is no boot-services table,
-ENOTSUPP
+ *    if a required protocol is not supported

%s/@return/Return:/

+ */
+static int setup_block(void)
+{
+    efi_guid_t efi_blkio_guid = EFI_BLOCK_IO_PROTOCOL_GUID;
+    efi_guid_t efi_devpath_guid = EFI_DEVICE_PATH_PROTOCOL_GUID;
+    efi_guid_t efi_pathutil_guid =
EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID;
+    efi_guid_t efi_pathtext_guid =
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
+    struct efi_boot_services *boot = efi_get_boot();
+    struct efi_device_path_utilities_protocol *util;
+    struct efi_device_path_to_text_protocol *text;
+    struct efi_device_path *path;
+    struct efi_block_io *blkio;
+    efi_uintn_t num_handles;
+    efi_handle_t *handle;
+    int ret, i;
+
+    if (!boot)
+        return log_msg_ret("sys", -ENOSYS);
+
+    /* Find all devices which support the block I/O protocol */
+    ret = boot->locate_handle_buffer(BY_PROTOCOL, &efi_blkio_guid, NULL,
+                  &num_handles, &handle);
+    if (ret)
+        return log_msg_ret("loc", -ENOTSUPP);
+    log_debug("Found %d handles:\n", (int)num_handles);
+
+    /* We need to look up the path size and convert it to text */
+    ret = boot->locate_protocol(&efi_pathutil_guid, NULL, (void
**)&util);
+    if (ret)
+        return log_msg_ret("util", -ENOTSUPP);
+    ret = boot->locate_protocol(&efi_pathtext_guid, NULL, (void
**)&text);
+    if (ret)
+        return log_msg_ret("text", -ENOTSUPP);
+
+    for (i = 0; i < num_handles; i++) {
+        struct udevice *dev;
+        const u16 *name;
+        bool is_part;
+        int len;
+
+        ret = boot->handle_protocol(handle[i], &efi_devpath_guid,
+                        (void **)&path);
+        if (ret) {
+            log_warning("- devpath %d failed (ret=%d)\n", i, ret);
+            continue;
+        }
+
+        ret = boot->handle_protocol(handle[i], &efi_blkio_guid,
+                        (void **)&blkio);
+        if (ret) {
+            log_warning("- blkio %d failed (ret=%d)\n", i, ret);
+            continue;
+        }
+
+        name = text->convert_device_path_to_text(path, true, false);
+        is_part = devpath_is_partition(path);
+
+        if (!is_part) {
+            len = util->get_device_path_size(path);
+            ret = efi_bind_block(handle[i], blkio, path, len, &dev);
+            if (ret) {
+                log_warning("- blkio bind %d failed (ret=%d)\n",
+                        i, ret);
+                continue;
+            }
+        } else {
+            dev = NULL;
+        }
+
+        /*
+         * Show the device name if we created one. Otherwise indicate
+         * that it is a partition.
+         *
+         * We don't seem to have have a way to print unicode on the
+         * U-Boot console at present, so use our own function.
+         */
+        printf("%2d: %-12s ", i, dev ? dev->name : "<partition>");
+        efi_print_str(name);
+        printf("\n");
+    }
+    boot->free_pool(handle);
+
+    return 0;
+}
+

Missing function description.

Best regards

Heinrich

+int dm_scan_other(bool pre_reloc_only)
+{
+    if (gd->flags & GD_FLG_RELOC) {
+        int ret;
+
+        ret = setup_block();
+        if (ret)
+            return ret;
+
+        /* Not needed at present, but could be useful one day? */
+        ret = setup_disks();
+        if (ret)
+            return ret;
+    }
+
+    return 0;
+}
+
  /**
   * efi_main() - Start an EFI image
   *



Reply via email to