[U-Boot] [PATCH v4 1/2] efi_driver: EFI block driver
Alexander Graf
agraf at suse.de
Mon Jan 22 13:52:45 UTC 2018
On 01/21/2018 07:29 PM, 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.glpk at gmx.de>
> ---
> v4
> Use the priv area instead for platdata.
> Use calloc() instead of passing a stack address.
> Use blk_find_max_devnum() to get next free device number.
> 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 | 210 ++++++++++++++++++++++++
> lib/efi_driver/efi_uclass.c | 330 ++++++++++++++++++++++++++++++++++++++
> 11 files changed, 595 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 a30259c4c1..51213c0293 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 563c7ba3cf..21c03c5c28 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..90f7581cfa
> --- /dev/null
> +++ b/lib/efi_driver/efi_block_device.c
> @@ -0,0 +1,210 @@
> +/*
> + * 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/device-internal.h>
> +#include <dm/root.h>
> +
> +/*
> + * EFI attributes of the udevice handled by this driver.
> + *
> + * handle handle of the controller on which this driver is installed
> + * io block io protocol proxied by this driver
> + */
> +struct efi_blk_priv {
> + efi_handle_t handle;
> + struct efi_block_io *io;
> +};
> +
> +/*
> + * 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_blk_priv *priv = dev->priv;
> + struct efi_block_io *io = priv->io;
> + 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_blk_priv *priv = dev->priv;
> + struct efi_block_io *io = priv->io;
> + 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;
> +}
> +
> +/*
> + * Create partions for the block device.
> + *
> + * @handle EFI handle of the block device
> + * @dev udevice of the block device
> + */
> +static int efi_bl_bind_partitions(efi_handle_t handle, struct udevice *dev)
> +{
> + struct blk_desc *desc;
> + const char *if_typename;
> +
> + desc = dev_get_uclass_platdata(dev);
> + if_typename = blk_get_if_type_name(desc->if_type);
> +
> + return efi_disk_create_partitions(handle, desc, if_typename,
> + desc->devnum, 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;
> + struct efi_object *obj = efi_search_obj(handle);
> + struct efi_block_io *io = interface;
> + int disks;
> + struct efi_blk_priv *priv;
> +
> + EFI_PRINT("%s: handle %p, interface %p\n", __func__, handle, io);
> +
> + if (!obj)
> + return -ENOENT;
> +
> + devnum = blk_find_max_devnum(IF_TYPE_EFI);
> + if (devnum == -ENODEV)
> + devnum = 0;
> + else if (devnum < 0)
> + return devnum;
> +
> + name = calloc(1, 18);
This should get explained a bit better. I've replaced the line with:
name = calloc(1, 18); /* strlen("efiblk#2147483648") + 1 */
I originally thought gcc would optimize strlen(const) away, but it
didn't for me, so I went with the comment.
Alex
More information about the U-Boot
mailing list