[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