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

Signed-off-by: Adriano Cordova <adriano.cord...@canonical.com>
---
 include/efi_loader.h            |  10 +-
 lib/efi_driver/Makefile         |   1 +
 lib/efi_driver/efi_net_device.c |  86 +++++++++++
 lib/efi_driver/efi_uclass.c     |   2 +-
 lib/efi_loader/efi_net.c        | 261 +++++++++++++++++++++++++-------
 lib/efi_loader/efi_setup.c      |   4 +-
 6 files changed, 302 insertions(+), 62 deletions(-)
 create mode 100644 lib/efi_driver/efi_net_device.c

diff --git a/include/efi_loader.h b/include/efi_loader.h
index 0d6b01fcfc..09a9f75e19 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -634,9 +634,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 0da20fe91d..08489064c7 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 0000000000..64a443d074
--- /dev/null
+++ b/lib/efi_driver/efi_net_device.c
@@ -0,0 +1,86 @@
+// 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 e1e28df20b..6553141f4e 100644
--- a/lib/efi_driver/efi_uclass.c
+++ b/lib/efi_driver/efi_uclass.c
@@ -74,7 +74,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 da0dabd383..20d2c3a5f6 100644
--- a/lib/efi_loader/efi_net.c
+++ b/lib/efi_loader/efi_net.c
@@ -859,6 +859,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());
@@ -1054,28 +1059,18 @@ 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;
 
@@ -1103,57 +1098,59 @@ 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_do_start() - start the efi network stack
+ *
+ * This gets called from do_bootefi_exec() each time a payload gets executed.
+ *
+ * Return:     status code
+ */
+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_net_register() - register the simple network protocol
+ * efi_netobj_init() - initialize an efi_net_obj
  *
- * This gets called from do_bootefi_exec().
- * @dev:       net udevice
+ * @netobj:    efi net object
+ * Return:     0 on success, negative on error
  */
-efi_status_t efi_net_register(struct udevice *dev)
+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;
        uchar **receive_buffer;
        size_t *receive_lengths;
        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;
-       }
-
-       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;
-               }
+               return 0;
        }
 
-       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)
@@ -1252,7 +1249,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;
        }
 
        /*
@@ -1268,13 +1265,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)
@@ -1288,15 +1285,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++)
@@ -1304,7 +1298,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 48f91da5df..e6ef76bcca 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.43.0

Reply via email to