[PATCH 01/16] efi_driver: efi_net: Add EFI network device driver

Adriano Cordova adrianox at gmail.com
Tue Mar 11 17:47:43 CET 2025


Register the U-boot ethernet udevices in the EFI network stack
as efi_net_obj when they get probed, and unregister them when the
udevice gets removed

Signed-off-by: Adriano Cordova <adriano.cordova at canonical.com>
---
 include/efi_loader.h            |  10 +-
 lib/efi_driver/Makefile         |   1 +
 lib/efi_driver/efi_net_device.c |  89 +++++++++++
 lib/efi_driver/efi_uclass.c     |   2 +-
 lib/efi_loader/efi_net.c        | 263 +++++++++++++++++++++++++-------
 lib/efi_loader/efi_setup.c      |   4 +-
 6 files changed, 305 insertions(+), 64 deletions(-)
 create mode 100644 lib/efi_driver/efi_net_device.c

diff --git a/include/efi_loader.h b/include/efi_loader.h
index e9c10819ba2..9c5ddc12cea 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -656,9 +656,13 @@ 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);
-/* Called by bootefi to make the network interface available */
-efi_status_t efi_net_register(struct udevice *dev);
-efi_status_t efi_net_do_start(struct udevice *dev);
+/* Called to register an EFI network device */
+int efi_net_register(void *ctx, struct event *event);
+/* Called to unregister an EFI network device */
+int efi_net_unregister(void *ctx, struct event *event);
+/* Called to initialized registered network devices */
+efi_status_t efi_net_init(void);
+efi_status_t efi_net_do_start(void);
 /* Called by efi_net_register to make the ip4 config2 protocol available */
 efi_status_t efi_ipconfig_register(const efi_handle_t handle,
 				   struct efi_ip4_config2_protocol *ip4config);
diff --git a/lib/efi_driver/Makefile b/lib/efi_driver/Makefile
index 0da20fe91d3..08489064c7a 100644
--- a/lib/efi_driver/Makefile
+++ b/lib/efi_driver/Makefile
@@ -9,4 +9,5 @@ obj-y += efi_uclass.o
 ifeq ($(CONFIG_PARTITIONS),y)
 obj-y += efi_block_device.o
 endif
+obj-$(CONFIG_NETDEVICES) += efi_net_device.o
 obj-$(CONFIG_SYSRESET_SBI) += efi_reset_riscv.o
diff --git a/lib/efi_driver/efi_net_device.c b/lib/efi_driver/efi_net_device.c
new file mode 100644
index 00000000000..f2167485ab9
--- /dev/null
+++ b/lib/efi_driver/efi_net_device.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  EFI network driver
+ *
+ */
+
+#include <dm.h>
+#include <efi_driver.h>
+#include <malloc.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
+#include <dm/tag.h>
+#include <dm/uclass-internal.h>
+
+/**
+ * efi_net_bind_drv() - TODO
+ *
+ * @this:	driver binding protocol
+ * @handle:	handle
+ * @interface:	block io protocol
+ * Return:	status code
+ */
+static efi_status_t efi_net_bind_drv(
+			struct efi_driver_binding_extended_protocol *this,
+			efi_handle_t handle, void *interface)
+{
+
+	EFI_PRINT("%s: handle %p, interface %p\n", __func__, handle, interface);
+
+
+	return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_net_init_drv() - initialize network device driver
+ *
+ * @this:	extended driver binding protocol
+ * Return:	status code
+ */
+static efi_status_t
+efi_net_init_drv(struct efi_driver_binding_extended_protocol *this)
+{
+	int ret;
+	struct udevice *dev;
+	struct event event;
+
+	for (uclass_find_first_device(UCLASS_ETH, &dev); dev;
+	     uclass_find_next_device(&dev)) {
+		if (dev_get_flags(dev) & DM_FLAG_ACTIVATED) {
+			memcpy(&event.data, &dev, sizeof(dev));
+			ret = efi_net_register(NULL, &event);
+			if (ret) {
+				log_err("Failed registering %s in EFI net\n", dev->name);
+				return EFI_OUT_OF_RESOURCES;
+			}
+		}
+
+	}
+
+	ret = event_register("efi_net register", EVT_DM_POST_PROBE,
+			     efi_net_register, this);
+	if (ret) {
+		log_err("Event registration for efi_net register failed\n");
+		return EFI_OUT_OF_RESOURCES;
+	}
+
+	ret = event_register("efi_net unregister", EVT_DM_PRE_REMOVE,
+			     efi_net_unregister, this);
+	if (ret) {
+		log_err("Event registration for efi_net unregister failed\n");
+		return EFI_OUT_OF_RESOURCES;
+	}
+
+	return EFI_SUCCESS;
+}
+
+/* EFI driver operators */
+static const struct efi_driver_ops driver_ops = {
+	.protocol	= &efi_net_guid,
+	.init		= efi_net_init_drv,
+	.bind		= efi_net_bind_drv,
+};
+
+/* Identify as EFI driver */
+U_BOOT_DRIVER(efi_net) = {
+	.name		= "efi_net",
+	.id		= UCLASS_EFI_LOADER,
+	.ops		= &driver_ops,
+};
diff --git a/lib/efi_driver/efi_uclass.c b/lib/efi_driver/efi_uclass.c
index 495be53cb77..fc8808374a1 100644
--- a/lib/efi_driver/efi_uclass.c
+++ b/lib/efi_driver/efi_uclass.c
@@ -76,7 +76,7 @@ static efi_status_t EFIAPI efi_uc_supported(
 	 * U-Boot internal devices install protocols interfaces without calling
 	 * ConnectController(). Hence we should not bind an extra driver.
 	 */
-	if (controller_handle->dev) {
+	if (controller_handle->dev && bp->ops->protocol != &efi_net_guid) {
 		ret = EFI_UNSUPPORTED;
 		goto out;
 	}
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c
index b3291b4f1d5..0334eedaf5c 100644
--- a/lib/efi_loader/efi_net.c
+++ b/lib/efi_loader/efi_net.c
@@ -865,6 +865,11 @@ static void EFIAPI efi_network_timer_notify(struct efi_event *event,
 	nt = efi_netobj_from_snp(this);
 	curr_efi_net_obj = nt->efi_seq_num;
 
+	// The following only happens if the net obj was removed but the event
+	// was not successfully removed.
+	if (!net_objs[curr_efi_net_obj] || !efi_netobj_is_active(nt))
+		goto out;
+
 	if (!nt->rx_packet_num) {
 		eth_set_dev(nt->dev);
 		env_set("ethact", eth_get_name());
@@ -1060,27 +1065,16 @@ static struct efi_device_path *efi_netobj_get_dp(struct efi_net_obj *netobj)
 }
 
 /**
- * efi_net_do_start() - start the efi network stack
+ * efi_netobj_start() - start an efi net device
  *
- * This gets called from do_bootefi_exec() each time a payload gets executed.
  *
- * @dev:	net udevice
+ * @netobj:	efi_net_obj
  * Return:	status code
  */
-efi_status_t efi_net_do_start(struct udevice *dev)
+efi_status_t efi_netobj_start(struct efi_net_obj *netobj)
 {
 	efi_status_t r = EFI_SUCCESS;
-	struct efi_net_obj *netobj;
 	struct efi_device_path *net_dp;
-	int i;
-
-	netobj = NULL;
-	for (i = 0; i < MAX_EFI_NET_OBJS; i++) {
-		if (net_objs[i] && net_objs[i]->dev == dev) {
-			netobj = net_objs[i];
-			break;
-		}
-	}
 
 	if (!efi_netobj_is_active(netobj))
 		return r;
@@ -1110,57 +1104,58 @@ set_addr:
 	 * but the PXE protocol is not yet implmenented, so we add this in the meantime.
 	 */
 	efi_net_get_addr((struct efi_ipv4_address *)&netobj->pxe_mode.station_ip,
-			 (struct efi_ipv4_address *)&netobj->pxe_mode.subnet_mask, NULL, dev);
+			 (struct efi_ipv4_address *)&netobj->pxe_mode.subnet_mask, NULL, netobj->dev);
 #endif
 
-	return r;
+	return 0;
 }
 
 /**
- * efi_net_register() - register the simple network protocol
+ * efi_net_do_start() - start the efi network stack
+ *
+ * This gets called from do_bootefi_exec() each time a payload gets executed.
  *
- * This gets called from do_bootefi_exec().
- * @dev:	net udevice
+ * Return:	status code
  */
-efi_status_t efi_net_register(struct udevice *dev)
+efi_status_t efi_net_do_start(void)
+{
+	int i, r;
+
+	for (i = 0; i < MAX_EFI_NET_OBJS; i++) {
+		if (net_objs[i]) {
+			r = efi_netobj_start(net_objs[i]);
+			if (r)
+				return EFI_DEVICE_ERROR;
+		}
+	}
+
+	return EFI_SUCCESS;
+}
+
+/**
+ * efi_netobj_init() - initialize an efi_net_obj
+ *
+ * @netobj:	efi net object
+ * Return:	0 on success, negative on error
+ */
+static int efi_netobj_init(struct efi_net_obj *netobj)
 {
 	efi_status_t r;
-	int seq_num;
-	struct efi_net_obj *netobj;
+	struct udevice *dev;
 	void *transmit_buffer = NULL;
 	uchar **receive_buffer = NULL;
-	size_t *receive_lengths;
+	size_t *receive_lengths = NULL;
 	int i, j;
 
+	if (!netobj || efi_netobj_is_active(netobj))
+		return 0;
+
+	dev = netobj->dev;
 	if (!dev) {
 		/* No network device active, don't expose any */
-		return EFI_SUCCESS;
+		return 0;
 	}
 
-	for (i = 0; i < MAX_EFI_NET_OBJS; i++) {
-		if (net_objs[i] && net_objs[i]->dev == dev) {
-			// Do not register duplicate devices
-			return EFI_SUCCESS;
-		}
-	}
-
-	seq_num = -1;
-	for (i = 0; i < MAX_EFI_NET_OBJS; i++) {
-		if (!net_objs[i]) {
-			seq_num = i;
-			break;
-		}
-	}
-	if (seq_num < 0)
-		return EFI_OUT_OF_RESOURCES;
-
-	/* We only expose the "active" network device, so one is enough */
-	netobj = calloc(1, sizeof(*netobj));
-	if (!netobj)
-		goto out_of_resources;
-
-	netobj->dev = dev;
-
 	/* Allocate an aligned transmit buffer */
 	transmit_buffer = calloc(1, PKTSIZE_ALIGN + PKTALIGN);
 	if (!transmit_buffer)
@@ -1259,7 +1254,7 @@ efi_status_t efi_net_register(struct udevice *dev)
 			     &netobj->wait_for_packet);
 	if (r != EFI_SUCCESS) {
 		printf("ERROR: Failed to register network event\n");
-		return r;
+		return -1;
 	}
 	netobj->net.wait_for_packet = netobj->wait_for_packet;
 	/*
@@ -1275,13 +1270,13 @@ efi_status_t efi_net_register(struct udevice *dev)
 			     &netobj->network_timer_event);
 	if (r != EFI_SUCCESS) {
 		printf("ERROR: Failed to register network event\n");
-		return r;
+		return -1;
 	}
 	/* Network is time critical, create event in every timer cycle */
 	r = efi_set_timer(netobj->network_timer_event, EFI_TIMER_PERIODIC, 0);
 	if (r != EFI_SUCCESS) {
 		printf("ERROR: Failed to set network timer\n");
-		return r;
+		return -1;
 	}
 
 #if IS_ENABLED(CONFIG_EFI_IP4_CONFIG2_PROTOCOL)
@@ -1295,15 +1290,12 @@ efi_status_t efi_net_register(struct udevice *dev)
 	if (r != EFI_SUCCESS)
 		goto failure_to_add_protocol;
 #endif
-	netobj->efi_seq_num = seq_num;
-	net_objs[seq_num] = netobj;
-	return EFI_SUCCESS;
+	printf("efi_net init device number %d\n", netobj->efi_seq_num);
+	return 0;
 failure_to_add_protocol:
 	printf("ERROR: Failure to add protocol\n");
-	return r;
+	return -1;
 out_of_resources:
-	free(netobj);
-	netobj = NULL;
 	free(transmit_buffer);
 	if (receive_buffer)
 		for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++)
@@ -1311,7 +1303,162 @@ out_of_resources:
 	free(receive_buffer);
 	free(receive_lengths);
 	printf("ERROR: Out of memory\n");
-	return EFI_OUT_OF_RESOURCES;
+	return -1;
+}
+
+/**
+ * efi_net_init() - initialize registered efi objects
+ *
+ * Return:	status code
+ */
+efi_status_t efi_net_init(void)
+{
+	int i, r;
+
+	for (i = 0; i < MAX_EFI_NET_OBJS; i++) {
+		if (net_objs[i]) {
+			r = efi_netobj_init(net_objs[i]);
+			if (r)
+				return EFI_DEVICE_ERROR;
+		}
+	}
+
+	return EFI_SUCCESS;
+}
+
+/**
+ * efi_net_register() - register a net device
+ *
+ * This function is called when the device is probed
+ *
+ * @ctx:	context set at registration time
+ * @event:	event
+ * Return:	0 on success, negative on error
+ */
+int efi_net_register(void *ctx, struct event *event)
+{
+	struct udevice *dev;
+	int seq_num;
+	enum uclass_id id;
+	struct efi_net_obj *netobj;
+	int i;
+
+	dev = event->data.dm.dev;
+	if (!dev) {
+		/* No network device active, don't expose any */
+		return 0;
+	}
+
+	id = device_get_uclass_id(dev);
+	if (id != UCLASS_ETH)
+		return 0;
+
+	for (i = 0; i < MAX_EFI_NET_OBJS; i++) {
+		if (net_objs[i] && net_objs[i]->dev == dev) {
+			// Do not register duplicate devices
+			return 0;
+		}
+	}
+
+	// Find a slot for this efi_net_obj
+	seq_num = -1;
+	for (i = 0; i < MAX_EFI_NET_OBJS; i++) {
+		if (!net_objs[i]) {
+			seq_num = i;
+			break;
+		}
+	}
+	if (seq_num < 0)
+		return -1;
+
+	netobj = calloc(1, sizeof(*netobj));
+	if (!netobj)
+		goto out_of_resources;
+
+	netobj->dev = dev;
+	netobj->efi_seq_num = seq_num;
+	net_objs[seq_num] = netobj;
+	printf("efi_net registered device number %d\n", netobj->efi_seq_num);
+	return 0;
+out_of_resources:
+	printf("ERROR: Out of memory\n");
+	return -1;
+}
+
+/**
+ * efi_net_unregister() - unregister a net device
+ *
+ *
+ * @ctx:	context set at registration time
+ * @event:	event
+ * Return:	0 on success, negative on error
+ */
+int efi_net_unregister(void *ctx, struct event *event)
+{
+	efi_status_t ret = EFI_SUCCESS;
+	struct udevice *dev;
+	enum uclass_id id;
+	struct efi_net_obj *netobj;
+	struct efi_handler *phandler;
+	void *interface;
+	int i;
+
+	dev = event->data.dm.dev;
+	if (!dev) {
+		/* No network device active, don't expose any */
+		return 0;
+	}
+
+	id = device_get_uclass_id(dev);
+	if (id != UCLASS_ETH)
+		return 0;
+
+	netobj = NULL;
+	for (i = 0; i < MAX_EFI_NET_OBJS; i++) {
+		if (net_objs[i] && net_objs[i]->dev == dev) {
+			netobj = net_objs[i];
+			break;
+		}
+	}
+
+	if (!netobj)
+		return 0;
+
+	// Remove from the list
+	net_objs[i] = NULL;
+
+	if (efi_netobj_is_active(netobj)) {
+		free(netobj->transmit_buffer);
+		if (netobj->receive_buffer)
+			for (i = 0; i < ETH_PACKETS_BATCH_RECV; i++)
+				free(netobj->receive_buffer[i]);
+		free(netobj->receive_buffer);
+		free(netobj->receive_lengths);
+
+		ret = EFI_CALL(efi_close_event(netobj->wait_for_packet));
+		if (ret != EFI_SUCCESS)
+			return -1;
+
+		ret = EFI_CALL(efi_close_event(netobj->network_timer_event));
+		if (ret != EFI_SUCCESS)
+			return -1;
+
+		phandler = NULL;
+		efi_search_protocol(&netobj->header, &efi_guid_device_path, &phandler);
+		if (phandler && phandler->protocol_interface)
+			interface = phandler->protocol_interface;
+
+		ret = efi_delete_handle(&netobj->header);
+		if (ret != EFI_SUCCESS)
+			return -1;
+
+		efi_free_pool(interface);
+	}
+
+	// Free the efi_net_obj
+	free(netobj);
+
+	return 0;
 }
 
 /**
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index 48f91da5df7..e6ef76bcca6 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -220,7 +220,7 @@ static efi_status_t efi_start_obj_list(void)
 	efi_status_t ret = EFI_SUCCESS;
 
 	if (IS_ENABLED(CONFIG_NETDEVICES))
-		ret = efi_net_do_start(eth_get_dev());
+		ret = efi_net_do_start();
 
 	return ret;
 }
@@ -337,7 +337,7 @@ efi_status_t efi_init_obj_list(void)
 			goto out;
 	}
 	if (IS_ENABLED(CONFIG_NETDEVICES)) {
-		ret = efi_net_register(eth_get_dev());
+		ret = efi_net_init();
 		if (ret != EFI_SUCCESS)
 			goto out;
 	}
-- 
2.48.1



More information about the U-Boot mailing list