On 19.01.18 20:24, Heinrich Schuchardt wrote:
> This patch provides
> * a uclass for EFI drivers
> * a EFI driver for block devices
> 
> For each EFI driver the uclass
> * creates a handle
> * adds the driver binding protocol
> 
> The uclass provides the bind, start, and stop entry points for the driver
> binding protocol.
> 
> In bind() and stop() it checks if the controller implements the protocol
> supported by the EFI driver. In the start() function it calls the bind()
> function of the EFI driver. In the stop() function it destroys the child
> controllers.
> 
> The EFI block driver binds to controllers implementing the block io
> protocol.
> 
> When the bind function of the EFI block driver is called it creates a
> new U-Boot block device. It installs child handles for all partitions and
> installs the simple file protocol on these.
> 
> The read and write functions of the EFI block driver delegate calls to the
> controller that it is bound to.
> 
> A usage example is as following:
> 
> U-Boot loads the iPXE snp.efi executable. iPXE connects an iSCSI drive and
> exposes a handle with the block IO protocol. It calls ConnectController.
> 
> Now the EFI block driver installs the partitions with the simple file
> protocol.
> 
> iPXE uses the simple file protocol to load Grub or the Linux Kernel.
> 
> Signed-off-by: Heinrich Schuchardt <xypron.g...@gmx.de>
> ---
> v3
>       Initalize EFI uclass from bootefi command.
>       Fix typos.
> v2
>       Print to console only in debug mode.
>       Provide more comments.
>       Add commit message.
> ---
>  cmd/bootefi.c                     |   3 +
>  drivers/block/blk-uclass.c        |   4 +-
>  include/blk.h                     |   1 +
>  include/config_fallbacks.h        |   1 +
>  include/dm/uclass-id.h            |   1 +
>  include/efi_driver.h              |  30 ++++
>  include/efi_loader.h              |   2 +
>  lib/Makefile                      |   1 +
>  lib/efi_driver/Makefile           |  13 ++
>  lib/efi_driver/efi_block_device.c | 175 ++++++++++++++++++++
>  lib/efi_driver/efi_uclass.c       | 330 
> ++++++++++++++++++++++++++++++++++++++
>  11 files changed, 560 insertions(+), 1 deletion(-)
>  create mode 100644 include/efi_driver.h
>  create mode 100644 lib/efi_driver/Makefile
>  create mode 100644 lib/efi_driver/efi_block_device.c
>  create mode 100644 lib/efi_driver/efi_uclass.c
> 
> diff --git a/cmd/bootefi.c b/cmd/bootefi.c
> index 78ff109835..f16d56eb59 100644
> --- a/cmd/bootefi.c
> +++ b/cmd/bootefi.c
> @@ -32,6 +32,9 @@ static void efi_init_obj_list(void)
>  {
>       efi_obj_list_initalized = 1;
>  
> +     /* Initialize EFI driver uclass */
> +     efi_driver_init();
> +
>       efi_console_register();
>  #ifdef CONFIG_PARTITIONS
>       efi_disk_register();
> diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c
> index 010ed32d3a..bfda2211f0 100644
> --- a/drivers/block/blk-uclass.c
> +++ b/drivers/block/blk-uclass.c
> @@ -24,6 +24,7 @@ static const char *if_typename_str[IF_TYPE_COUNT] = {
>       [IF_TYPE_HOST]          = "host",
>       [IF_TYPE_SYSTEMACE]     = "ace",
>       [IF_TYPE_NVME]          = "nvme",
> +     [IF_TYPE_EFI]           = "efi",
>  };
>  
>  static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
> @@ -36,8 +37,9 @@ static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
>       [IF_TYPE_SD]            = UCLASS_INVALID,
>       [IF_TYPE_SATA]          = UCLASS_AHCI,
>       [IF_TYPE_HOST]          = UCLASS_ROOT,
> -     [IF_TYPE_NVME]          = UCLASS_NVME,
>       [IF_TYPE_SYSTEMACE]     = UCLASS_INVALID,
> +     [IF_TYPE_NVME]          = UCLASS_NVME,
> +     [IF_TYPE_EFI]           = UCLASS_EFI,
>  };
>  
>  static enum if_type if_typename_to_iftype(const char *if_typename)
> diff --git a/include/blk.h b/include/blk.h
> index 41b4d7efa8..69b5a98e56 100644
> --- a/include/blk.h
> +++ b/include/blk.h
> @@ -34,6 +34,7 @@ enum if_type {
>       IF_TYPE_HOST,
>       IF_TYPE_SYSTEMACE,
>       IF_TYPE_NVME,
> +     IF_TYPE_EFI,
>  
>       IF_TYPE_COUNT,                  /* Number of interface types */
>  };
> diff --git a/include/config_fallbacks.h b/include/config_fallbacks.h
> index 2c4d43d672..524313d5aa 100644
> --- a/include/config_fallbacks.h
> +++ b/include/config_fallbacks.h
> @@ -52,6 +52,7 @@
>       defined(CONFIG_MMC) || \
>       defined(CONFIG_NVME) || \
>       defined(CONFIG_SYSTEMACE) || \
> +     (defined(CONFIG_EFI_LOADER) && !defined(CONFIG_SPL_BUILD)) || \
>       defined(CONFIG_SANDBOX)
>  #define HAVE_BLOCK_DEVICE
>  #endif
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index 3fc20834ae..07fabc3ce6 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -34,6 +34,7 @@ enum uclass_id {
>       UCLASS_CROS_EC,         /* Chrome OS EC */
>       UCLASS_DISPLAY,         /* Display (e.g. DisplayPort, HDMI) */
>       UCLASS_DMA,             /* Direct Memory Access */
> +     UCLASS_EFI,             /* EFI managed devices */
>       UCLASS_ETH,             /* Ethernet device */
>       UCLASS_GPIO,            /* Bank of general-purpose I/O pins */
>       UCLASS_FIRMWARE,        /* Firmware */
> diff --git a/include/efi_driver.h b/include/efi_driver.h
> new file mode 100644
> index 0000000000..2bbe26c6e3
> --- /dev/null
> +++ b/include/efi_driver.h
> @@ -0,0 +1,30 @@
> +/*
> + *  EFI application loader
> + *
> + *  Copyright (c) 2017 Heinrich Schuchardt
> + *
> + *  SPDX-License-Identifier:     GPL-2.0+
> + */
> +
> +#ifndef _EFI_DRIVER_H
> +#define _EFI_DRIVER_H 1
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <efi_loader.h>
> +
> +struct efi_driver_ops {
> +     const efi_guid_t *protocol;
> +     const efi_guid_t *child_protocol;
> +     int (*bind)(efi_handle_t handle, void *interface);
> +};
> +
> +/*
> + * This structure adds internal fields to the driver binding protocol.
> + */
> +struct efi_driver_binding_extended_protocol {
> +     struct efi_driver_binding_protocol bp;
> +     const struct efi_driver_ops *ops;
> +};
> +
> +#endif /* _EFI_DRIVER_H */
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 035b04fef4..8b11f30edf 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -271,6 +271,8 @@ efi_status_t efi_get_memory_map(efi_uintn_t 
> *memory_map_size,
>  /* Adds a range into the EFI memory map */
>  uint64_t efi_add_memory_map(uint64_t start, uint64_t pages, int memory_type,
>                           bool overlap_only_ram);
> +/* Called by board init to initialize the EFI drivers */
> +int efi_driver_init(void);
>  /* Called by board init to initialize the EFI memory map */
>  int efi_memory_init(void);
>  /* Adds new or overrides configuration table entry to the system table */
> diff --git a/lib/Makefile b/lib/Makefile
> index 8cd779f8ca..0db41c19f3 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -8,6 +8,7 @@
>  ifndef CONFIG_SPL_BUILD
>  
>  obj-$(CONFIG_EFI) += efi/
> +obj-$(CONFIG_EFI_LOADER) += efi_driver/
>  obj-$(CONFIG_EFI_LOADER) += efi_loader/
>  obj-$(CONFIG_EFI_LOADER) += efi_selftest/
>  obj-$(CONFIG_LZMA) += lzma/
> diff --git a/lib/efi_driver/Makefile b/lib/efi_driver/Makefile
> new file mode 100644
> index 0000000000..e35529a952
> --- /dev/null
> +++ b/lib/efi_driver/Makefile
> @@ -0,0 +1,13 @@
> +#
> +# (C) Copyright 2017 Heinrich Schuchardt
> +#
> +#  SPDX-License-Identifier:     GPL-2.0+
> +#
> +
> +# This file only gets included with CONFIG_EFI_LOADER set, so all
> +# object inclusion implicitly depends on it
> +
> +obj-y += efi_uclass.o
> +ifeq ($(CONFIG_BLK)$(CONFIG_PARTITIONS),yy)
> +obj-y += efi_block_device.o
> +endif
> diff --git a/lib/efi_driver/efi_block_device.c 
> b/lib/efi_driver/efi_block_device.c
> new file mode 100644
> index 0000000000..f614560abc
> --- /dev/null
> +++ b/lib/efi_driver/efi_block_device.c
> @@ -0,0 +1,175 @@
> +/*
> + *  EFI block driver
> + *
> + *  Copyright (c) 2017 Heinrich Schuchardt
> + *
> + *  SPDX-License-Identifier:     GPL-2.0+
> + *
> + * The EFI uclass creates a handle for this driver and installs the
> + * driver binding protocol on it.
> + *
> + * The EFI block driver binds to controllers implementing the block io
> + * protocol.
> + *
> + * When the bind function of the EFI block driver is called it creates a
> + * new U-Boot block device. It installs child handles for all partitions and
> + * installs the simple file protocol on these.
> + *
> + * The read and write functions of the EFI block driver delegate calls to the
> + * controller that it is bound to.
> + *
> + * A usage example is as following:
> + *
> + * U-Boot loads the iPXE snp.efi executable. iPXE connects an iSCSI drive and
> + * exposes a handle with the block IO protocol. It calls ConnectController.
> + *
> + * Now the EFI block driver installs the partitions with the simple file
> + * protocol.
> + *
> + * iPXE uses the simple file protocol to load Grub or the Linux Kernel.
> + */
> +
> +#include <efi_driver.h>
> +#include <dm/root.h>
> +
> +static int efi_blk_max_devnum;
> +
> +/*
> + * Read from block device
> + *
> + * @dev              device
> + * @blknr    first block to be read
> + * @blkcnt   number of blocks to read
> + * @buffer   output buffer
> + * @return   number of blocks transferred
> + */
> +static ulong efi_bl_read(struct udevice *dev, lbaint_t blknr, lbaint_t 
> blkcnt,
> +                      void *buffer)
> +{
> +     struct efi_block_io *io = dev->platdata;
> +     efi_status_t ret;
> +
> +     EFI_PRINT("%s: read '%s', from block " LBAFU ", " LBAFU " blocks\n",
> +               __func__, dev->name, blknr, blkcnt);
> +     ret = EFI_CALL(io->read_blocks(
> +                             io, io->media->media_id, (u64)blknr,
> +                             (efi_uintn_t)blkcnt *
> +                             (efi_uintn_t)io->media->block_size, buffer));
> +     EFI_PRINT("%s: r = %u\n", __func__,
> +               (unsigned int)(ret & ~EFI_ERROR_MASK));
> +     if (ret != EFI_SUCCESS)
> +             return 0;
> +     return blkcnt;
> +}
> +
> +/*
> + * Write to block device
> + *
> + * @dev              device
> + * @blknr    first block to be write
> + * @blkcnt   number of blocks to write
> + * @buffer   input buffer
> + * @return   number of blocks transferred
> + */
> +static ulong efi_bl_write(struct udevice *dev, lbaint_t blknr, lbaint_t 
> blkcnt,
> +                       const void *buffer)
> +{
> +     struct efi_block_io *io = dev->platdata;
> +     efi_status_t ret;
> +
> +     EFI_PRINT("%s: write '%s', from block " LBAFU ", " LBAFU " blocks\n",
> +               __func__, dev->name, blknr, blkcnt);
> +     ret = EFI_CALL(io->write_blocks(
> +                             io, io->media->media_id, (u64)blknr,
> +                             (efi_uintn_t)blkcnt *
> +                             (efi_uintn_t)io->media->block_size,
> +                             (void *)buffer));
> +     EFI_PRINT("%s: r = %u\n", __func__,
> +               (unsigned int)(ret & ~EFI_ERROR_MASK));
> +     if (ret != EFI_SUCCESS)
> +             return 0;
> +     return blkcnt;
> +}
> +
> +static int efi_bl_bind_partitions(efi_handle_t handle)
> +{
> +     struct efi_object *obj = efi_search_obj(handle);
> +     struct blk_desc *desc;
> +     const char *if_typename;
> +
> +     if (!obj || !obj->dev)
> +             return -ENOENT;
> +     desc = dev_get_uclass_platdata(obj->dev);
> +     if_typename = blk_get_if_type_name(desc->if_type);
> +
> +     return efi_disk_create_partitions(handle, desc, if_typename,
> +                                       desc->devnum, obj->dev->name);
> +}
> +
> +/*
> + * Create a block device for a handle
> + *
> + * @handle   handle
> + * @interface        block io protocol
> + * @return   0 = success
> + */
> +static int efi_bl_bind(efi_handle_t handle, void *interface)
> +{
> +     struct udevice *bdev, *parent = dm_root();
> +     int ret, devnum;
> +     char name[20];
> +     struct efi_object *obj = efi_search_obj(handle);
> +     struct efi_block_io *io = interface;
> +     int disks;
> +
> +     EFI_PRINT("%s: handle %p, interface %p\n", __func__, handle, io);
> +
> +     if (!obj)
> +             return -ENOENT;
> +
> +     devnum = efi_blk_max_devnum++;

Can you store this in the "EFI block driver" instance? Maybe you want to
pass a pointer to that to the bind function anyway?

> +     sprintf(name, "efi#%d", devnum);
> +

/* Create U-Boot DM device for the EFI block backing device */

> +     ret = blk_create_device(parent, "efi_blk", name, IF_TYPE_EFI, devnum,
> +                             io->media->block_size,
> +                             (lbaint_t)io->media->last_block, &bdev);
> +     if (ret)
> +             return ret;
> +     EFI_PRINT("%s: block device '%s' created\n", __func__, bdev->name);
> +     bdev->platdata = interface;

Can you instead create a priv struct that contains the pointer to
interface as a member? Then use that instead of platdata.

Platdata really is supposed to store information that lives in device
tree these days. Parameters to your device basically.

> +     obj->dev = bdev;

Please integrate my patches to remove all references to obj->dev.

> +
> +     ret = blk_prepare_device(bdev);
> +

/* Also create EFI devices for all partitions on top of the EFI block
device */

> +     disks = efi_bl_bind_partitions(handle);
> +     EFI_PRINT("Found %d partitions\n", disks);
> +
> +     return 0;



Alex
_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to