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.cord...@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,

Reply via email to