[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