[PATCH 05/10] Add EFI network driver

Matthew Garrett mjg59 at srcf.ucam.org
Sat Nov 23 20:55:04 CET 2024


From: Matthew Garrett <mgarrett at aurora.tech>

Add a driver that makes use of the UEFI Simple Network Protocol to
support network access when using the UEFI app implementation, and hook
up the app code to instantiate it for probed devices.

Signed-off-by: Matthew Garrett <mgarrett at aurora.tech>
---

 drivers/net/Kconfig    |   7 +++
 drivers/net/Makefile   |   1 +
 drivers/net/efi_net.c  | 110 +++++++++++++++++++++++++++++++++++++++++
 include/efi.h          |  12 +++++
 lib/efi/efi_app_init.c |  67 +++++++++++++++++++++++++
 5 files changed, 197 insertions(+)
 create mode 100644 drivers/net/efi_net.c

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 576cd2d50ad..693d00fa2e3 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -333,6 +333,13 @@ config EEPRO100
 	  This driver supports Intel(R) PRO/100 82557/82559/82559ER fast
 	  ethernet family of adapters.
 
+config EFI_NET
+        bool "UEFI firmware-based networking support"
+	depends on EFI_APP
+	help
+	  This driver allows the use of UEFI-provided network interfaces
+	  as network devices within U-Boot.
+
 config ESSEDMA
 	bool "Qualcomm ESS Edma support"
 	depends on DM_ETH && ARCH_IPQ40XX
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index f5ab1f5dedf..f58c62cdb40 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_DWC_ETH_QOS_STM32) += dwc_eth_qos_stm32.o
 obj-$(CONFIG_E1000) += e1000.o
 obj-$(CONFIG_E1000_SPI) += e1000_spi.o
 obj-$(CONFIG_EEPRO100) += eepro100.o
+obj-$(CONFIG_EFI_NET) += efi_net.o
 obj-$(CONFIG_ESSEDMA) += essedma.o
 obj-$(CONFIG_ETHOC) += ethoc.o
 obj-$(CONFIG_ETH_DESIGNWARE) += designware.o
diff --git a/drivers/net/efi_net.c b/drivers/net/efi_net.c
new file mode 100644
index 00000000000..370056040f3
--- /dev/null
+++ b/drivers/net/efi_net.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Aurora Innovation, Inc. Copyright 2022.
+ *
+ */
+
+#include <config.h>
+#include <dm.h>
+#include <efi.h>
+#include <efi_api.h>
+#include <malloc.h>
+#include <net.h>
+
+struct efi_net_priv {
+	void *buffer;
+};
+
+static int efi_net_bind(struct udevice *dev)
+{
+	struct efi_net_plat *plat = dev_get_plat(dev);
+	struct efi_net_priv *priv = dev_get_priv(dev);
+	efi_status_t status;
+
+	priv->buffer = malloc(PKTSIZE);
+
+	status = plat->snp->start(plat->snp);
+	if (status != EFI_SUCCESS)
+		return -ENOENT;
+
+	return 0;
+}
+
+static int efi_net_init(struct udevice *dev)
+{
+	struct efi_net_plat *plat = dev_get_plat(dev);
+	efi_status_t status;
+
+	status = plat->snp->initialize(plat->snp, 0, 0);
+	if (status != EFI_SUCCESS)
+		return -ENOENT;
+
+	return 0;
+}
+
+static void efi_net_halt(struct udevice *dev)
+{
+	struct efi_net_plat *plat = dev_get_plat(dev);
+
+	plat->snp->shutdown(plat->snp);
+	plat->snp->stop(plat->snp);
+}
+
+static int efi_net_send(struct udevice *dev, void *packet, int length)
+{
+	struct efi_net_plat *plat = dev_get_plat(dev);
+	efi_status_t status;
+
+	status = plat->snp->transmit(plat->snp, 0, length, packet, NULL, NULL,
+				     NULL);
+	if (status != EFI_SUCCESS)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int efi_net_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+	struct efi_net_plat *plat = dev_get_plat(dev);
+	struct efi_net_priv *priv = dev_get_priv(dev);
+	efi_status_t status;
+	size_t size = PKTSIZE;
+
+	status = plat->snp->receive(plat->snp, 0, &size, priv->buffer, NULL,
+				    NULL, NULL);
+	if (status == EFI_NOT_READY)
+		return -EAGAIN;
+	else if (status != EFI_SUCCESS)
+		return -EINVAL;
+
+	*packetp = priv->buffer;
+
+	return 0;
+}
+
+static int efi_net_read_hwaddr(struct udevice *dev)
+{
+	struct efi_net_plat *plat = dev_get_plat(dev);
+
+	memcpy(plat->eth_pdata.enetaddr,
+	       (void *)&plat->snp->mode->permanent_address, ARP_HLEN);
+
+	return 0;
+}
+
+static const struct eth_ops efi_net_ops = {
+	.start	= efi_net_init,
+	.stop	= efi_net_halt,
+	.send	= efi_net_send,
+	.recv	= efi_net_recv,
+	.read_rom_hwaddr = efi_net_read_hwaddr,
+};
+
+U_BOOT_DRIVER(efi_net) = {
+	.name	= "efi_net",
+	.id	= UCLASS_ETH,
+	.bind	= efi_net_bind,
+	.ops	= &efi_net_ops,
+	.plat_auto = sizeof(struct efi_net_plat),
+	.priv_auto = sizeof(struct efi_net_priv),
+};
diff --git a/include/efi.h b/include/efi.h
index 1d06230439f..3c4c321362f 100644
--- a/include/efi.h
+++ b/include/efi.h
@@ -16,6 +16,7 @@
 #ifndef _EFI_H
 #define _EFI_H
 
+#include <net.h>
 #include <linux/linkage.h>
 #include <linux/string.h>
 #include <linux/types.h>
@@ -494,6 +495,17 @@ struct efi_media_plat {
 	struct efi_device_path *device_path;
 };
 
+/*
+ * EFI attributes of the udevice handled by efi_net driver
+ *
+ * @handle: handle of the controller on which this driver is installed
+ */
+struct efi_net_plat {
+	struct eth_pdata eth_pdata;
+	efi_handle_t handle;
+	struct efi_simple_network *snp;
+};
+
 /* Base address of the EFI image */
 extern char image_base[];
 
diff --git a/lib/efi/efi_app_init.c b/lib/efi/efi_app_init.c
index c5e4192fe06..bc09eb063a1 100644
--- a/lib/efi/efi_app_init.c
+++ b/lib/efi/efi_app_init.c
@@ -183,6 +183,70 @@ static int setup_block(void)
 	return 0;
 }
 
+/**
+ * setup_net() - Find all network devices and setup EFI devices for them
+ *
+ * Return: 0 if found, -ENOSYS if there is no boot-services table, -ENOTSUPP
+ *	if a required protocol is not supported
+ */
+static int setup_net(void)
+{
+	efi_guid_t efi_snp_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
+	struct efi_boot_services *boot = efi_get_boot();
+	efi_uintn_t num_handles;
+	efi_handle_t *handle;
+	int ret, i;
+
+	if (!boot)
+		return log_msg_ret("sys", -ENOSYS);
+
+	/* Find all devices which support the simple network protocol */
+	ret = boot->locate_handle_buffer(BY_PROTOCOL, &efi_snp_guid, NULL,
+				  &num_handles, &handle);
+
+	if (ret)
+		return 0;
+	log_debug("Found %d net handles:\n", (int)num_handles);
+
+	for (i = 0; i < num_handles; i++) {
+		struct efi_simple_network *snp;
+		struct efi_net_plat *plat;
+		struct udevice *dev;
+		char name[18];
+
+		ret = boot->handle_protocol(handle[i], &efi_snp_guid,
+					    (void **)&snp);
+
+		if (ret != EFI_SUCCESS) {
+			log_warning("- snp %d failed (ret=0x%x\n", i, ret);
+			continue;
+		}
+
+		plat = malloc(sizeof(*plat));
+		if (!plat) {
+			log_warning("- snp %d failed to alloc platform data", i);
+			continue;
+		}
+		plat->handle = handle[i];
+		plat->snp = snp;
+		ret = device_bind(dm_root(), DM_DRIVER_GET(efi_net), "efi_net",
+				  plat, ofnode_null(), &dev);
+		if (ret) {
+			log_warning("- bind snp %d failed (ret=0x%x)\n", i,
+				    ret);
+			continue;
+		}
+
+		snprintf(name, sizeof(name), "efi_net_%x", dev_seq(dev));
+		device_set_name(dev, name);
+
+		printf("%2d: %-12s\n", i, dev->name);
+	}
+	boot->free_pool(handle);
+
+	return 0;
+}
+
 /**
  * board_early_init_r() - Scan for UEFI devices that should be available
  *
@@ -199,6 +263,9 @@ int board_early_init_r(void)
 		ret = setup_block();
 		if (ret)
 			return ret;
+		ret = setup_net();
+		if (ret)
+			return ret;
 	}
 
 	return 0;
-- 
2.47.0



More information about the U-Boot mailing list