[PATCH v3 45/70] bootstd: Allow reading an EFI file from the network

Simon Glass sjg at chromium.org
Tue Jan 17 18:47:55 CET 2023


At present this bootmeth only supports reading from a filesystem. Add
support for reading from a network also, using DHCP with autoload.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

(no changes since v1)

 boot/bootmeth_efi.c | 117 +++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 109 insertions(+), 8 deletions(-)

diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c
index f7bb153d9de..77b4ba22470 100644
--- a/boot/bootmeth_efi.c
+++ b/boot/bootmeth_efi.c
@@ -19,6 +19,7 @@
 #include <malloc.h>
 #include <mapmem.h>
 #include <mmc.h>
+#include <net.h>
 #include <pxe_utils.h>
 
 #define EFI_DIRNAME	"efi/boot/"
@@ -59,6 +60,40 @@ static int get_efi_leafname(char *str, int max_len)
 	return 0;
 }
 
+static int get_efi_pxe_arch(void)
+{
+	/* http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml */
+	if (IS_ENABLED(CONFIG_ARM64))
+		return 0xb;
+	else if (IS_ENABLED(CONFIG_ARM))
+		return 0xa;
+	else if (IS_ENABLED(CONFIG_X86_64))
+		return 0x6;
+	else if (IS_ENABLED(CONFIG_X86))
+		return 0x7;
+	else if (IS_ENABLED(CONFIG_ARCH_RV32I))
+		return 0x19;
+	else if (IS_ENABLED(CONFIG_ARCH_RV64I))
+		return 0x1b;
+	else if (IS_ENABLED(CONFIG_SANDBOX))
+		return 0;	/* not used */
+
+	return -EINVAL;
+}
+
+static int get_efi_pxe_vci(char *str, int max_len)
+{
+	int ret;
+
+	ret = get_efi_pxe_arch();
+	if (ret < 0)
+		return ret;
+
+	snprintf(str, max_len, "PXEClient:Arch:%05x:UNDI:003000", ret);
+
+	return 0;
+}
+
 static int efiload_read_file(struct blk_desc *desc, struct bootflow *bflow)
 {
 	const struct udevice *media_dev;
@@ -101,20 +136,18 @@ static int efiload_read_file(struct blk_desc *desc, struct bootflow *bflow)
 
 static int distro_efi_check(struct udevice *dev, struct bootflow_iter *iter)
 {
-	int ret;
-
-	/* This only works on block devices */
-	ret = bootflow_iter_check_blk(iter);
-	if (ret)
-		return log_msg_ret("blk", ret);
+	/* This only works on block and network devices */
+	if (bootflow_iter_check_blk(iter) && bootflow_iter_check_net(iter))
+		return log_msg_ret("blk", -ENOTSUPP);
 
 	return 0;
 }
 
-static int distro_efi_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+static int distro_efi_read_bootflow_file(struct udevice *dev,
+					 struct bootflow *bflow)
 {
 	struct blk_desc *desc = NULL;
-	char fname[sizeof(EFI_DIRNAME) + 16];
+	char fname[256];
 	int ret;
 
 	/* We require a partition table */
@@ -140,6 +173,74 @@ static int distro_efi_read_bootflow(struct udevice *dev, struct bootflow *bflow)
 	return 0;
 }
 
+static int distro_efi_read_bootflow_net(struct bootflow *bflow)
+{
+	const char *addr_str;
+	int ret, arch, size;
+	char str[36];
+	ulong addr;
+
+	ret = get_efi_pxe_vci(str, sizeof(str));
+	if (ret)
+		return log_msg_ret("vci", ret);
+	ret = get_efi_pxe_arch();
+	if (ret < 0)
+		return log_msg_ret("arc", ret);
+	arch = ret;
+
+	ret = env_set("bootp_vci", str);
+	if (ret)
+		return log_msg_ret("vcs", ret);
+	ret = env_set_ulong("bootp_arch", arch);
+	if (ret)
+		return log_msg_ret("ars", ret);
+
+	/* figure out the load address */
+	addr_str = env_get("kernel_addr_r");
+	addr = addr_str ? hextoul(addr_str, NULL) : image_load_addr;
+
+	/* clear any previous bootfile */
+	env_set("bootfile", NULL);
+
+	/* read the kernel */
+	ret = dhcp_run(addr, NULL, true);
+	if (ret)
+		return log_msg_ret("dhc", ret);
+
+	size = env_get_hex("filesize", -1);
+	if (size <= 0)
+		return log_msg_ret("sz", -EINVAL);
+	bflow->size = size;
+
+	/* do the hideous EFI hack */
+	efi_set_bootdev("Net", "", bflow->fname, map_sysmem(addr, 0),
+			bflow->size);
+
+	bflow->state = BOOTFLOWST_READY;
+
+	return 0;
+}
+
+static int distro_efi_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+{
+	const struct udevice *media = dev_get_parent(bflow->dev);
+	int ret;
+
+	if (IS_ENABLED(CONFIG_CMD_DHCP) &&
+	    device_get_uclass_id(media) == UCLASS_ETH) {
+		/* we only support reading from one device, so ignore 'dev' */
+		ret = distro_efi_read_bootflow_net(bflow);
+		if (ret)
+			return log_msg_ret("net", ret);
+	} else {
+		ret = distro_efi_read_bootflow_file(dev, bflow);
+		if (ret)
+			return log_msg_ret("blk", ret);
+	}
+
+	return 0;
+}
+
 int distro_efi_boot(struct udevice *dev, struct bootflow *bflow)
 {
 	char cmd[50];
-- 
2.39.0.246.g2a6d74b583-goog



More information about the U-Boot mailing list