[U-Boot] [PATCH v3 17/18] efi_driver: EFI block driver
Alexander Graf
agraf at suse.de
Fri Jan 19 21:03:00 UTC 2018
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.glpk at 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
More information about the U-Boot
mailing list