[U-Boot] [PATCH v2 16/18] efi_driver: EFI block driver

Alexander Graf agraf at suse.de
Fri Jan 19 17:03:40 UTC 2018



On 18.01.18 21:08, Heinrich Schuchardt wrote:
> On 01/17/2018 08:16 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 partions 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>
>> ---
>> v2
>> 	Print to console only in debug mode.
>> 	Provide more comments.
>> 	Add commit message.
>> ---
>>  common/board_r.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/common/board_r.c b/common/board_r.c
>> index 2baa47f3a0..4ad37ee31a 100644
>> --- a/common/board_r.c
>> +++ b/common/board_r.c
>> @@ -715,7 +715,10 @@ static init_fnc_t init_sequence_r[] = {
>>  	set_cpu_clk_info, /* Setup clock information */
>>  #endif
>>  #ifdef CONFIG_EFI_LOADER
>> +	/* Setup EFI memory before any other EFI related code */
>>  	efi_memory_init,
>> +	/* Install EFI drivers */
>> +	efi_driver_init,
>>  #endif
>>  	stdio_init_tables,
>>  	initr_serial,
>> 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 711c901eda..a465175d1f 100644
>> --- a/include/efi_loader.h
>> +++ b/include/efi_loader.h
>> @@ -273,6 +273,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..837787d563
>> --- /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 partions 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_partions(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++;
>> +	sprintf(name, "efi#%d", devnum);
>> +
>> +	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;
>> +	obj->dev = bdev;
>> +
>> +	ret = blk_prepare_device(bdev);
>> +
>> +	disks = efi_bl_bind_partions(handle);
>> +	EFI_PRINT("Found %d partions\n", disks);
>> +
>> +	return 0;
>> +}
>> +
>> +/* Block device driver operators */
>> +static const struct blk_ops efi_blk_ops = {
>> +	.read	= efi_bl_read,
>> +	.write	= efi_bl_write,
>> +};
>> +
>> +/* Identify as block device driver */
>> +U_BOOT_DRIVER(efi_blk) = {
>> +	.name		= "efi_blk",
>> +	.id		= UCLASS_BLK,
>> +	.ops		= &efi_blk_ops,
>> +};
>> +
>> +/* EFI driver operators */
>> +static const struct efi_driver_ops driver_ops = {
>> +	.protocol	= &efi_block_io_guid,
>> +	.child_protocol = &efi_block_io_guid,
>> +	.bind		= efi_bl_bind,
>> +};
>> +
>> +/* Identify as EFI driver */
>> +U_BOOT_DRIVER(efi_block) = {
>> +	.name		= "EFI block driver",
>> +	.id		= UCLASS_EFI,
>> +	.ops		= &driver_ops,
>> +};
>> diff --git a/lib/efi_driver/efi_uclass.c b/lib/efi_driver/efi_uclass.c
>> new file mode 100644
>> index 0000000000..798431ae74
>> --- /dev/null
>> +++ b/lib/efi_driver/efi_uclass.c
>> @@ -0,0 +1,330 @@
>> +/*
>> + *  Uclass for EFI drivers
>> + *
>> + *  Copyright (c) 2017 Heinrich Schuchardt
>> + *
>> + *  SPDX-License-Identifier:     GPL-2.0+
>> + *
>> + * For each EFI driver the uclass
>> + * - creates a handle
>> + * - installs 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.
>> + */
>> +
>> +#include <efi_driver.h>
>> +
>> +/*
>> + * Check node type. We do not support partions as controller handles.
>> + *
>> + * @handle	handle to be checked
>> + * @return	status code
>> + */
>> +static efi_status_t check_node_type(efi_handle_t handle)
>> +{
>> +	efi_status_t r, ret = EFI_SUCCESS;
>> +	const struct efi_device_path *dp;
>> +
>> +	/* Open the device path protocol */
>> +	r = EFI_CALL(systab.boottime->open_protocol(
>> +			handle, &efi_guid_device_path, (void **)&dp,
>> +			NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL));
>> +	if (r == EFI_SUCCESS && dp) {
>> +		/* Get the last node */
>> +		const struct efi_device_path *node = efi_dp_last_node(dp);
>> +		/* We do not support partitions as controller */
>> +		if (!node || node->type == DEVICE_PATH_TYPE_MEDIA_DEVICE)
>> +			ret = EFI_UNSUPPORTED;
>> +	}
>> +	return ret;
>> +}
>> +
>> +/*
>> + * Check if the driver supports the controller.
>> + *
>> + * @this			driver binding protocol
>> + * @controller_handle		handle of the controller
>> + * @remaining_device_path	path specifying the child controller
>> + * @return			status code
>> + */
>> +static efi_status_t EFIAPI efi_uc_supported(
>> +		struct efi_driver_binding_protocol *this,
>> +		efi_handle_t controller_handle,
>> +		struct efi_device_path *remaining_device_path)
>> +{
>> +	efi_status_t r, ret;
>> +	void *interface;
>> +	struct efi_driver_binding_extended_protocol *bp =
>> +			(struct efi_driver_binding_extended_protocol *)this;
>> +
>> +	EFI_ENTRY("%p, %p, %ls", this, controller_handle,
>> +		  efi_dp_str(remaining_device_path));
>> +
>> +	ret = EFI_CALL(systab.boottime->open_protocol(
>> +			controller_handle, bp->ops->protocol,
>> +			&interface, this->driver_binding_handle,
>> +			controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER));
>> +	switch (ret) {
>> +	case EFI_ACCESS_DENIED:
>> +	case EFI_ALREADY_STARTED:
>> +		goto out;
>> +	case EFI_SUCCESS:
>> +		break;
>> +	default:
>> +		ret = EFI_UNSUPPORTED;
>> +		goto out;
>> +	}
>> +
>> +	ret = check_node_type(controller_handle);
>> +
>> +	r = EFI_CALL(systab.boottime->close_protocol(
>> +				controller_handle, bp->ops->protocol,
>> +				this->driver_binding_handle,
>> +				controller_handle));
>> +	if (r != EFI_SUCCESS)
>> +		ret = EFI_UNSUPPORTED;
>> +out:
>> +	return EFI_EXIT(ret);
>> +}
>> +
>> +/*
>> + * Create child controllers and attach driver.
>> + *
>> + * @this			driver binding protocol
>> + * @controller_handle		handle of the controller
>> + * @remaining_device_path	path specifying the child controller
>> + * @return			status code
>> + */
>> +static efi_status_t EFIAPI efi_uc_start(
>> +		struct efi_driver_binding_protocol *this,
>> +		efi_handle_t controller_handle,
>> +		struct efi_device_path *remaining_device_path)
>> +{
>> +	efi_status_t r, ret;
>> +	void *interface = NULL;
>> +	struct efi_driver_binding_extended_protocol *bp =
>> +			(struct efi_driver_binding_extended_protocol *)this;
>> +
>> +	EFI_ENTRY("%p, %pUl, %ls", this, controller_handle,
>> +		  efi_dp_str(remaining_device_path));
>> +
>> +	/* Attach driver to controller */
>> +	ret = EFI_CALL(systab.boottime->open_protocol(
>> +			controller_handle, bp->ops->protocol,
>> +			&interface, this->driver_binding_handle,
>> +			controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER));
>> +	switch (ret) {
>> +	case EFI_ACCESS_DENIED:
>> +	case EFI_ALREADY_STARTED:
>> +		goto out;
>> +	case EFI_SUCCESS:
>> +		break;
>> +	default:
>> +		ret =  EFI_UNSUPPORTED;
>> +		goto out;
>> +	}
>> +	ret = check_node_type(controller_handle);
>> +	if (ret != EFI_SUCCESS) {
>> +		r = EFI_CALL(systab.boottime->close_protocol(
>> +				controller_handle, bp->ops->protocol,
>> +				this->driver_binding_handle,
>> +				controller_handle));
>> +		if (r != EFI_SUCCESS)
>> +			EFI_PRINT("Failure to close handle\n");
>> +		goto out;
>> +	}
>> +
>> +	/* TODO: driver specific stuff */
>> +	bp->ops->bind(controller_handle, interface);
>> +
>> +out:
>> +	return EFI_EXIT(ret);
>> +}
>> +
>> +/*
>> + * Remove a single child controller from the parent controller.
>> + *
>> + * @controller_handle	parent controller
>> + * @child_handle	child controller
>> + * @return		status code
>> + */
>> +static efi_status_t disconnect_child(efi_handle_t controller_handle,
>> +				     efi_handle_t child_handle)
>> +{
>> +	efi_status_t ret;
>> +	efi_guid_t *guid_controller = NULL;
>> +	efi_guid_t *guid_child_controller = NULL;
>> +
>> +	ret = EFI_CALL(systab.boottime->close_protocol(
>> +				controller_handle, guid_controller,
>> +				child_handle, child_handle));
>> +	if (ret != EFI_SUCCESS) {
>> +		EFI_PRINT("Cannot close protocol\n");
>> +		return ret;
>> +	}
>> +	ret = EFI_CALL(systab.boottime->uninstall_protocol_interface(
>> +				child_handle, guid_child_controller, NULL));
>> +	if (ret != EFI_SUCCESS) {
>> +		EFI_PRINT("Cannot uninstall protocol interface\n");
>> +		return ret;
>> +	}
>> +	return ret;
>> +}
>> +
>> +/*
>> + * Remove child controllers and disconnect the controller.
>> + *
>> + * @this			driver binding protocol
>> + * @controller_handle		handle of the controller
>> + * @number_of_children		number of child controllers to remove
>> + * @child_handle_buffer		handles of the child controllers to remove
>> + * @return			status code
>> + */
>> +static efi_status_t EFIAPI efi_uc_stop(
>> +		struct efi_driver_binding_protocol *this,
>> +		efi_handle_t controller_handle,
>> +		size_t number_of_children,
>> +		efi_handle_t *child_handle_buffer)
>> +{
>> +	efi_status_t ret;
>> +	efi_uintn_t count;
>> +	struct efi_open_protocol_info_entry *entry_buffer;
>> +	efi_guid_t *guid_controller = NULL;
>> +
>> +	EFI_ENTRY("%p, %pUl, %zu, %p", this, controller_handle,
>> +		  number_of_children, child_handle_buffer);
>> +
>> +	/* Destroy provided child controllers */
>> +	if (number_of_children) {
>> +		efi_uintn_t i;
>> +
>> +		for (i = 0; i < number_of_children; ++i) {
>> +			ret = disconnect_child(controller_handle,
>> +					       child_handle_buffer[i]);
>> +			if (ret != EFI_SUCCESS)
>> +				return ret;
>> +		}
>> +		return EFI_SUCCESS;
>> +	}
>> +
>> +	/* Destroy all children */
>> +	ret = EFI_CALL(systab.boottime->open_protocol_information(
>> +					controller_handle, guid_controller,
>> +					&entry_buffer, &count));
>> +	if (ret != EFI_SUCCESS)
>> +		goto out;
>> +	while (count) {
>> +		if (entry_buffer[--count].attributes &
>> +		    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
>> +			ret = disconnect_child(
>> +					controller_handle,
>> +					entry_buffer[count].agent_handle);
>> +			if (ret != EFI_SUCCESS)
>> +				goto out;
>> +		}
>> +	}
>> +	ret = EFI_CALL(systab.boottime->free_pool(entry_buffer));
>> +	if (ret != EFI_SUCCESS)
>> +		printf("%s(%u) %s: ERROR: Cannot free pool\n",
>> +		       __FILE__, __LINE__, __func__);
>> +
>> +	/* Detach driver from controller */
>> +	ret = EFI_CALL(systab.boottime->close_protocol(
>> +			controller_handle, guid_controller,
>> +			this->driver_binding_handle, controller_handle));
>> +out:
>> +	return EFI_EXIT(ret);
>> +}
>> +
>> +static efi_status_t efi_add_driver(struct driver *drv)
>> +{
>> +	efi_status_t ret;
>> +	const struct efi_driver_ops *ops = drv->ops;
>> +	struct efi_driver_binding_extended_protocol *bp;
>> +
>> +	debug("EFI: Adding driver '%s'\n", drv->name);
>> +	if (!ops->protocol) {
>> +		printf("EFI: ERROR: protocol GUID missing for driver '%s'\n",
>> +		       drv->name);
>> +		return EFI_INVALID_PARAMETER;
>> +	}
>> +	bp = calloc(1, sizeof(struct efi_driver_binding_extended_protocol));
>> +	if (!bp)
>> +		return EFI_OUT_OF_RESOURCES;
>> +
>> +	bp->bp.supported = efi_uc_supported;
>> +	bp->bp.start = efi_uc_start;
>> +	bp->bp.stop = efi_uc_stop;
>> +	bp->bp.version = 0xffffffff;
>> +	bp->ops = drv->ops;
>> +
>> +	ret = efi_create_handle(&bp->bp.driver_binding_handle);
>> +	if (ret != EFI_SUCCESS) {
>> +		free(bp);
>> +		goto out;
>> +	}
>> +	bp->bp.image_handle = bp->bp.driver_binding_handle;
>> +	ret = efi_add_protocol(bp->bp.driver_binding_handle,
>> +			       &efi_guid_driver_binding_protocol, bp);
>> +	if (ret != EFI_SUCCESS) {
>> +		efi_delete_handle(bp->bp.driver_binding_handle);
>> +		free(bp);
>> +		goto out;
>> +	}
>> +out:
>> +	return ret;
>> +}
>> +
>> +/*
>> + * Initialize the EFI drivers.
>> + * Called by board_init_r().
> 
> Hello Alex,
> 
> in a chat you asked why this is not called in efi_init_obj_list() after
> entering the bootefi command.
> 
> My architectural perspective is that in future we will create the EFI
> handles from the device tree in parallel to creating the udevices and
> use ConnectController to install the EFI drivers (e.g. for the simple
> network protocol in the case of network interfaces). This is only
> possible if the EFI uclass exists before udevices are created.

I'm not 100% sure that's the right direction yet, let's just leave it in
bootefi for now.


Alex


More information about the U-Boot mailing list