[PATCH 08/16] efi_driver: efi_net: UCLASS_ETH driver for EFI net devices

Heinrich Schuchardt xypron.glpk at gmx.de
Mon Apr 14 17:47:08 CEST 2025


On 11.03.25 17:47, Adriano Cordova wrote:
> Ethernet driver that uses an underlying efi_simple_network_protocol
> to send packages.
> 
> Signed-off-by: Adriano Cordova <adriano.cordova at canonical.com>
> ---
>   include/efi_loader.h            |  12 +++
>   lib/efi_driver/efi_net_device.c | 159 ++++++++++++++++++++++++++++++++
>   2 files changed, 171 insertions(+)
> 
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 38ea0c5c672..35f500fd97d 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -656,6 +656,18 @@ int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc,
>   			       const char *pdevname);
>   /* Called by bootefi to make GOP (graphical) interface available */
>   efi_status_t efi_gop_register(void);
> +
> +/**
> + * struct efi_netdev_plat - attributes of a network device
> + *
> + * @handle:	handle of the controller on which this driver is installed
> + * @snp:		simple network protocol proxied by this driver
> + */
> +struct efi_netdev_plat {
> +	efi_handle_t handle;
> +	struct efi_simple_network *snp;
> +	void *buffer;
> +};
>   /* Called to register an EFI network device */
>   int efi_net_register(void *ctx, struct event *event);
>   /* Called to unregister an EFI network device */
> diff --git a/lib/efi_driver/efi_net_device.c b/lib/efi_driver/efi_net_device.c
> index 90d695da2b1..3838cc47158 100644
> --- a/lib/efi_driver/efi_net_device.c
> +++ b/lib/efi_driver/efi_net_device.c
> @@ -7,11 +7,146 @@
>   #include <dm.h>
>   #include <efi_driver.h>
>   #include <malloc.h>
> +#include <net.h>
>   #include <dm/device-internal.h>
>   #include <dm/root.h>
>   #include <dm/tag.h>
>   #include <dm/uclass-internal.h>
>   
> +#define DEFAULT_EFI_NET_BUFFER_SIZE 1024
> +
> +int efi_net_start(struct udevice *dev)

This function should be static.
The function description is missing.

For debugging it is preferable that function names are unique. That way 
we can more easily set break points.

> +{
> +	struct efi_netdev_plat *plat;
> +	efi_status_t ret;
> +
> +	plat = dev_get_plat(dev);
> +	if (!plat || !plat->snp)
> +		return -1;

We would never arrive here if probing failed.

> +
> +	ret = plat->snp->start(plat->snp);
> +	if (ret != EFI_SUCCESS)
> +		return -1;
> +	ret = plat->snp->initialize(plat->snp, 0, 0);

Shouldn't efi_net_start() in lib/efi_loader/efi_net.c take care of this?

> +	if (ret != EFI_SUCCESS)
> +		return -1;
> +
> +	return 0;
> +}
> +
> +int efi_net_send(struct udevice *dev, void *packet, int length)
> +{

This function should be static.
The function description is missing.

> +	struct efi_netdev_plat *plat;
> +	efi_status_t ret;
> +
> +	plat = dev_get_plat(dev);
> +	if (!plat || !plat->snp)
> +		return -1;

We would never arrive here if probing failed.

> +
> +	ret = plat->snp->transmit(plat->snp, 0, length, packet, NULL, NULL, NULL);
> +	if (ret != EFI_SUCCESS)
> +		return -1;
> +
> +	return 0;
> +}
> +
> +int efi_net_recv(struct udevice *dev, int flags, uchar **packetp)
> +{
> +	struct efi_netdev_plat *plat;
> +	efi_status_t ret;
> +	size_t buffer_size;
> +
> +	plat = dev_get_plat(dev);
> +	if (!plat || !plat->snp)
> +		return -1;
> +
> +	if (plat->buffer)
> +		free(plat->buffer);
> +
> +	buffer_size = DEFAULT_EFI_NET_BUFFER_SIZE;
> +	plat->buffer = calloc(1, buffer_size);

You are allocating new memory on every call to efi_net_recv() but not 
freeing it.

calloc() may return NULL.

> +	ret = plat->snp->receive(plat->snp, NULL, &buffer_size, plat->buffer,
> +				 NULL, NULL, NULL);

efi_net_recv is time critical. We should avoid allocating it during 
transfers. Please, try to allocate it when probing the device and free 
it when removing it.

> +
> +	if (ret == EFI_BUFFER_TOO_SMALL) {
> +		free(plat->buffer);
> +		plat->buffer = calloc(1, buffer_size);

calloc() may return NULL.

> +		ret = plat->snp->receive(plat->snp, NULL, &buffer_size, plat->buffer,
> +					 NULL, NULL, NULL);
> +	}
> +
> +	if (ret != EFI_SUCCESS || ret != EFI_NOT_READY)
> +		return -1;
> +
> +	*packetp = plat->buffer;
> +	return buffer_size;
> +}
> +
> +void efi_net_stop(struct udevice *dev)
> +{
> +	struct efi_netdev_plat *plat;
> +
> +	plat = dev_get_plat(dev);
> +	if (!plat || !plat->snp)
> +		return;

We would never arrive here if probing failed.

> +
> +	plat->snp->stop(plat->snp);
> +}
> +
> +/**
> + * efi_netdev_create() - create a net udevice for a handle
> + *
> + * @handle:	handle
> + * @interface:	simple network protocol
> + * Return:	status code
> + */
> +static efi_status_t
> +efi_netdev_create(efi_handle_t handle, void *interface)
> +{
> +	struct udevice *dev = NULL, *parent = dm_root();
> +	efi_status_t ret;
> +	char *name;
> +	struct efi_netdev_plat *plat;
> +	static int devnum;
> +
> +	name = calloc(1, 18); /* strlen("efinet#2147483648") + 1 */
> +	if (!name)
> +		return EFI_OUT_OF_RESOURCES;
> +	sprintf(name, "efinet#%d", devnum);
> +	devnum++;
> +
> +	/* Create driver model udevice for the EFI block io device */

"block io"?

> +	if (eth_create_device(parent, "efi_netdev", name, &dev)) {
> +		ret = EFI_OUT_OF_RESOURCES;
> +		free(name);
> +		goto err;
> +	}
> +
> +	plat = dev_get_plat(dev);
> +	plat->handle = handle;
> +	plat->snp = interface;
> +
> +	if (efi_link_dev(handle, dev)) {
> +		ret = EFI_OUT_OF_RESOURCES;
> +		goto err;
> +	}
> +
> +	if (device_probe(dev)) {
> +		ret = EFI_DEVICE_ERROR;
> +		goto err;
> +	}

CCing Simon due to his interest in the driver model.

Best regards

Heinrich

> +	EFI_PRINT("%s: net udevice '%s' created\n", __func__, dev->name);
> +
> +	return EFI_SUCCESS;
> +
> +err:
> +	efi_unlink_dev(handle);
> +	if (dev)
> +		device_unbind(dev);
> +
> +	return ret;
> +}
> +
>   /**
>    * efi_net_bind_drv() - TODO
>    *
> @@ -26,6 +161,14 @@ static efi_status_t efi_net_bind_drv(
>   {
>   	EFI_PRINT("%s: handle %p, interface %p\n", __func__, handle, interface);
>   
> +	efi_status_t ret;
> +
> +	if (!handle->dev) {
> +		ret = efi_netdev_create(handle, interface);
> +		if (ret != EFI_SUCCESS)
> +			return ret;
> +	}
> +
>   	return EFI_SUCCESS;
>   }
>   
> @@ -72,6 +215,22 @@ efi_net_init_drv(struct efi_driver_binding_extended_protocol *this)
>   	return EFI_SUCCESS;
>   }
>   
> +/* Net device driver operators */
> +static const struct eth_ops efi_eth_ops = {
> +	.start	= efi_net_start,
> +	.send	= efi_net_send,
> +	.recv	= efi_net_recv,
> +	.stop	= efi_net_stop,
> +};
> +
> +/* Identify as net device driver */
> +U_BOOT_DRIVER(efi_netdev) = {
> +	.name		= "efi_netdev",
> +	.id		= UCLASS_ETH,
> +	.ops		= &efi_eth_ops,
> +	.plat_auto	= sizeof(struct efi_netdev_plat),
> +};
> +
>   /* EFI driver operators */
>   static const struct efi_driver_ops driver_ops = {
>   	.protocol	= &efi_net_guid,



More information about the U-Boot mailing list