[PATCH 20/28] bootmethod: Add an implementation of distro boot

Simon Glass sjg at chromium.org
Thu Aug 19 05:45:53 CEST 2021


Add a bootmethod type which handles distro boot, so we can boot a bootflow
using this commonly used mechanism.

In effect, this provides the same functionality as the 'pxe' and 'sysboot'
commands and shares the same code. But the interface into it is via a
bootmethod.

For now this requires the 'pxe' command be enabled.

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

 MAINTAINERS          |   2 +
 boot/Kconfig         |  11 +++
 boot/Makefile        |   1 +
 boot/bootmethod.c    |  14 ++++
 boot/distro.c        | 194 +++++++++++++++++++++++++++++++++++++++++++
 include/bootmethod.h |   2 +
 include/distro.h     |  62 ++++++++++++++
 7 files changed, 286 insertions(+)
 create mode 100644 boot/distro.c
 create mode 100644 include/distro.h

diff --git a/MAINTAINERS b/MAINTAINERS
index d977cee562b..f78141c138b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -634,7 +634,9 @@ BOOTMETHOD
 M:	Simon Glass <sjg at chromium.org>
 S:	Maintained
 F:	boot/bootmethod.c
+F:	boot/distro.c
 F:	include/bootmethod.h
+F:	include/distro.h
 
 BTRFS
 M:	Marek Behun <marek.behun at nic.cz>
diff --git a/boot/Kconfig b/boot/Kconfig
index 90f716c3ef1..8565fbeab84 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -299,6 +299,17 @@ config BOOTMETHOD
 	  iterating through available bootmethods to find a bootflow suitable
 	  for booting.
 
+config BOOTMETHOD_DISTRO
+	bool "Bootmethod support for distro boot"
+	depends on BOOTMETHOD && CMD_PXE
+	default y
+	help
+	  Enables support for distro boot using bootmethods. This makes the
+	  bootmethods look for a 'extlinux/extlinux.conf' on each filesystem
+	  they scan.
+
+	  This provides a way to try out bootmethod on an existing boot flow.
+
 config LEGACY_IMAGE_FORMAT
 	bool "Enable support for the legacy image format"
 	default y if !FIT_SIGNATURE
diff --git a/boot/Makefile b/boot/Makefile
index 10d427e115c..4ce721242b0 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -23,6 +23,7 @@ obj-y += image.o
 obj-$(CONFIG_ANDROID_AB) += android_ab.o
 obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o image-android-dt.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETHOD) += bootmethod.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTMETHOD_DISTRO) += distro.o
 obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o
 obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += fdt_region.o
 obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o
diff --git a/boot/bootmethod.c b/boot/bootmethod.c
index 8e4157c6a47..be9d4aa02cc 100644
--- a/boot/bootmethod.c
+++ b/boot/bootmethod.c
@@ -7,6 +7,7 @@
 #include <common.h>
 #include <blk.h>
 #include <bootmethod.h>
+#include <distro.h>
 #include <dm.h>
 #include <fs.h>
 #include <log.h>
@@ -34,6 +35,7 @@ static const char *const bootmethod_state[BOOTFLOWST_COUNT] = {
 };
 
 static const char *const bootmethod_type[BOOTFLOWT_COUNT] = {
+	"distro-boot",
 };
 
 int bootmethod_get_state(struct bootflow_state **statep)
@@ -360,6 +362,12 @@ int bootmethod_find_in_blk(struct udevice *dev, struct udevice *blk, int seq,
 
 	bflow->state = BOOTFLOWST_FS;
 
+	if (CONFIG_IS_ENABLED(BOOTMETHOD_DISTRO)) {
+		ret = distro_boot_setup(desc, partnum, bflow);
+		if (ret)
+			return log_msg_ret("distro", ret);
+	}
+
 	return 0;
 }
 
@@ -372,6 +380,12 @@ int bootflow_boot(struct bootflow *bflow)
 		return log_msg_ret("load", -EPROTO);
 
 	switch (bflow->type) {
+	case BOOTFLOWT_DISTRO:
+		if (CONFIG_IS_ENABLED(BOOTMETHOD_DISTRO)) {
+			done = true;
+			ret = distro_boot(bflow);
+		}
+		break;
 	case BOOTFLOWT_COUNT:
 		break;
 	}
diff --git a/boot/distro.c b/boot/distro.c
new file mode 100644
index 00000000000..062a8535ef1
--- /dev/null
+++ b/boot/distro.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * distro boot implementation for bootflow
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg at chromium.org>
+ */
+
+#include <common.h>
+#include <blk.h>
+#include <bootmethod.h>
+#include <command.h>
+#include <distro.h>
+#include <dm.h>
+#include <fs.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <net.h>
+#include <pxe_utils.h>
+#include <vsprintf.h>
+
+#define DISTRO_FNAME	"extlinux/extlinux.conf"
+
+/**
+ * struct distro_info - useful information for distro_getfile()
+ *
+ * @bflow: bootflow being booted
+ */
+struct distro_info {
+	struct bootflow *bflow;
+};
+
+static int distro_net_getfile(struct pxe_context *ctx, const char *file_path,
+			      char *file_addr, ulong *sizep)
+{
+	char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
+	int ret;
+
+	printf("get %s %s\n", file_addr, file_path);
+	tftp_argv[1] = file_addr;
+	tftp_argv[2] = (void *)file_path;
+
+	if (do_tftpb(ctx->cmdtp, 0, 3, tftp_argv))
+		return -ENOENT;
+	ret = pxe_get_file_size(sizep);
+	if (ret)
+		return log_msg_ret("tftp", ret);
+
+	return 0;
+}
+
+int distro_net_setup(struct bootflow *bflow)
+{
+	const char *addr_str;
+	char fname[200];
+	char *bootdir;
+	ulong addr;
+	ulong size;
+	char *buf;
+	int ret;
+
+	addr_str = env_get("pxefile_addr_r");
+	if (!addr_str)
+		return log_msg_ret("pxeb", -EPERM);
+	addr = simple_strtoul(addr_str, NULL, 16);
+
+	bflow->type = BOOTFLOWT_DISTRO;
+	ret = pxe_get(addr, &bootdir, &size);
+	if (ret)
+		return log_msg_ret("pxeb", ret);
+	bflow->size = size;
+
+	/* Use the directory of the dhcp bootdir as our subdir, if provided */
+	if (bootdir) {
+		const char *last_slash;
+		int path_len;
+
+		last_slash = strrchr(bootdir, '/');
+		if (last_slash) {
+			path_len = (last_slash - bootdir) + 1;
+			bflow->subdir = malloc(path_len + 1);
+			memcpy(bflow->subdir, bootdir, path_len);
+			bflow->subdir[path_len] = '\0';
+		}
+	}
+	snprintf(fname, sizeof(fname), "%s%s",
+		 bflow->subdir ? bflow->subdir : "", DISTRO_FNAME);
+
+	bflow->fname = strdup(fname);
+	if (!bflow->fname)
+		return log_msg_ret("name", -ENOMEM);
+
+	bflow->state = BOOTFLOWST_LOADED;
+
+	/* Allocate the buffer, including the \0 byte added by get_pxe_file() */
+	buf = malloc(size + 1);
+	if (!buf)
+		return log_msg_ret("buf", -ENOMEM);
+	memcpy(buf, map_sysmem(addr, 0), size + 1);
+	bflow->buf = buf;
+
+	return 0;
+}
+
+int distro_boot_setup(struct blk_desc *desc, int partnum,
+		      struct bootflow *bflow)
+{
+	loff_t size, bytes_read;
+	ulong addr;
+	char *buf;
+	int ret;
+
+	bflow->type = BOOTFLOWT_DISTRO;
+	bflow->fname = strdup(DISTRO_FNAME);
+	if (!bflow->fname)
+		return log_msg_ret("name", -ENOMEM);
+	ret = fs_size(bflow->fname, &size);
+	if (ret)
+		return log_msg_ret("size", ret);
+	bflow->state = BOOTFLOWST_FILE;
+	bflow->size = size;
+	log_debug("   - distro file size %x\n", (uint)size);
+	if (size > 0x10000)
+		return log_msg_ret("chk", -E2BIG);
+
+	/* Sadly FS closes the file after fs_size() so we must redo this */
+	ret = fs_set_blk_dev_with_part(desc, partnum);
+	if (ret)
+		return log_msg_ret("set", ret);
+
+	buf = malloc(size + 1);
+	if (!buf)
+		return log_msg_ret("buf", -ENOMEM);
+	addr = map_to_sysmem(buf);
+
+	ret = fs_read(bflow->fname, addr, 0, 0, &bytes_read);
+	if (ret) {
+		free(buf);
+		return log_msg_ret("read", ret);
+	}
+	if (size != bytes_read)
+		return log_msg_ret("bread", -EINVAL);
+	buf[size] = '\0';
+	bflow->state = BOOTFLOWST_LOADED;
+	bflow->buf = buf;
+
+	return 0;
+}
+
+static int disto_getfile(struct pxe_context *ctx, const char *file_path,
+			 char *file_addr, ulong *sizep)
+{
+	struct distro_info *info = ctx->userdata;
+	struct bootflow *bflow = info->bflow;
+	struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
+	loff_t len_read;
+	ulong addr;
+	int ret;
+
+	addr = simple_strtoul(file_addr, NULL, 16);
+	ret = fs_set_blk_dev_with_part(desc, bflow->part);
+	if (ret)
+		return ret;
+	ret = fs_read(file_path, addr, 0, 0, &len_read);
+	if (ret)
+		return ret;
+	*sizep = len_read;
+
+	return 0;
+}
+
+int distro_boot(struct bootflow *bflow)
+{
+	struct cmd_tbl cmdtp = {};	/* dummy */
+	struct pxe_context ctx;
+	struct distro_info info;
+	bool is_net = !bflow->blk;
+	ulong addr;
+	int ret;
+
+	addr = map_to_sysmem(bflow->buf);
+	info.bflow = bflow;
+	ret = pxe_setup_ctx(&ctx, &cmdtp,
+			    is_net ? distro_net_getfile : disto_getfile,
+			    &info, !is_net, bflow->subdir);
+	if (ret)
+		return log_msg_ret("ctx", -EINVAL);
+
+	ret = pxe_process(&ctx, addr, false);
+	if (ret)
+		return log_msg_ret("bread", -EINVAL);
+
+	return 0;
+}
diff --git a/include/bootmethod.h b/include/bootmethod.h
index a45897b0c0c..d80be556b8a 100644
--- a/include/bootmethod.h
+++ b/include/bootmethod.h
@@ -24,6 +24,8 @@ enum bootflow_state_t {
 };
 
 enum bootflow_type_t {
+	BOOTFLOWT_DISTRO,	/**< Distro boot */
+
 	BOOTFLOWT_COUNT,
 };
 
diff --git a/include/distro.h b/include/distro.h
new file mode 100644
index 00000000000..80b8da4bed3
--- /dev/null
+++ b/include/distro.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg at chromium.org>
+ */
+
+#ifndef __distro_h
+#define __distro_h
+
+struct blk_desc;
+
+/**
+ * distro_boot_setup() - Set up a bootflow for distro boot from a block device
+ *
+ * This fills out a bootflow for a particular boot device and partition. It
+ * scans for a filesystem and suitable file, updating the bootflow accordingly.
+ *
+ * This sets the following fields in @bflow:
+ *
+ *	type, size, fname, state, subdir, buf
+ *
+ * The caller mast have already set the other fields.
+ *
+ * @desc: Block-device descriptor
+ * @partnum: Partition number (1..)
+ * @bflow: Partial bootflow to be completed by this function
+ * @return 0 on success (bootflow got to 'loaded' state), -ve on error
+ */
+int distro_boot_setup(struct blk_desc *desc, int partnum,
+		      struct bootflow *bflow);
+
+/**
+ * distro_boot_setup() - Set up a bootflow for distro boot from a network device
+ *
+ * This fills out a bootflow for a network device. It scans the tftp server for
+ * a suitable file, updating the bootflow accordingly.
+ *
+ * At present no control is provided as to which network device is used.
+ *
+ * This sets the following fields in @bflow:
+ *
+ *	type, size, fname, state,, buf
+ *
+ * The caller mast have already set the other fields.
+ *
+ * @bflow: Partial bootflow to be completed by this function
+ * @return 0 on success (bootflow got to 'loaded' state), -ve on error
+ */
+int distro_net_setup(struct bootflow *bflow);
+
+/**
+ * distro_boot() - Boot a distro
+ *
+ * Boots a bootflow of type BOOTFLOWT_DISTRO. This typically needs to load more
+ * files as it processes and this is done via the same media as the bootflow
+ * was loaded
+ *
+ * @bflow: Bootflow to boot
+*/
+int distro_boot(struct bootflow *bflow);
+
+#endif
-- 
2.33.0.rc1.237.g0d66db33f3-goog



More information about the U-Boot mailing list