[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