Merge tag 'dm-next-14aug23' of https://source.denx.de/u-boot/custodians/u-boot-dm into next

Enhance bootmeth_cros
diff --git a/arch/x86/include/asm/zimage.h b/arch/x86/include/asm/zimage.h
index 9ad74dc..655675b 100644
--- a/arch/x86/include/asm/zimage.h
+++ b/arch/x86/include/asm/zimage.h
@@ -62,41 +62,4 @@
 int setup_zimage(struct boot_params *setup_base, char *cmd_line, int auto_boot,
 		 ulong initrd_addr, ulong initrd_size, ulong cmdline_force);
 
-/**
- * zimage_dump() - Dump the metadata of a zimage
- *
- * This shows all available information in a zimage that has been loaded.
- *
- * @base_ptr: Pointer to the boot parameters, typically at address
- *	DEFAULT_SETUP_BASE
- */
-void zimage_dump(struct boot_params *base_ptr);
-
-/**
- * zboot_start() - Boot a zimage
- *
- * Boot a zimage, given the component parts
- *
- * @addr: Address where the bzImage is moved before booting, either
- *	BZIMAGE_LOAD_ADDR or ZIMAGE_LOAD_ADDR
- * @base: Pointer to the boot parameters, typically at address
- *	DEFAULT_SETUP_BASE
- * @initrd: Address of the initial ramdisk, or 0 if none
- * @initrd_size: Size of the initial ramdisk, or 0 if none
- * @cmdline: Command line to use for booting
- * Return: -EFAULT on error (normally it does not return)
- */
-int zboot_start(ulong addr, ulong size, ulong initrd, ulong initrd_size,
-		ulong base, char *cmdline);
-
-/*
- * zimage_get_kernel_version() - Get the version string from a kernel
- *
- * @params: boot_params pointer
- * @kernel_base: base address of kernel
- * Return: Kernel version as a NUL-terminated string
- */
-const char *zimage_get_kernel_version(struct boot_params *params,
-				      void *kernel_base);
-
 #endif
diff --git a/arch/x86/lib/zimage.c b/arch/x86/lib/zimage.c
index 062e3d3..a41e1cc 100644
--- a/arch/x86/lib/zimage.c
+++ b/arch/x86/lib/zimage.c
@@ -692,7 +692,7 @@
 	printf("\n");
 }
 
-void zimage_dump(struct boot_params *base_ptr)
+void zimage_dump(struct boot_params *base_ptr, bool show_cmdline)
 {
 	struct setup_header *hdr;
 	const char *version;
@@ -703,7 +703,7 @@
 
 	printf("E820: %d entries\n", base_ptr->e820_entries);
 	if (base_ptr->e820_entries) {
-		printf("%18s  %16s  %s\n", "Addr", "Size", "Type");
+		printf("%12s  %10s  %s\n", "Addr", "Size", "Type");
 		for (i = 0; i < base_ptr->e820_entries; i++) {
 			struct e820_entry *entry = &base_ptr->e820_map[i];
 
@@ -749,7 +749,7 @@
 	print_num("Ext loader ver", hdr->ext_loader_ver);
 	print_num("Ext loader type", hdr->ext_loader_type);
 	print_num("Command line ptr", hdr->cmd_line_ptr);
-	if (hdr->cmd_line_ptr) {
+	if (show_cmdline && hdr->cmd_line_ptr) {
 		printf("   ");
 		/* Use puts() to avoid limits from CONFIG_SYS_PBSIZE */
 		puts((char *)(ulong)hdr->cmd_line_ptr);
@@ -787,7 +787,7 @@
 		printf("No zboot setup_base\n");
 		return CMD_RET_FAILURE;
 	}
-	zimage_dump(base_ptr);
+	zimage_dump(base_ptr, true);
 
 	return 0;
 }
diff --git a/boot/Kconfig b/boot/Kconfig
index b00d7a8..5e2d428 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -464,8 +464,8 @@
 
 config BOOTMETH_CROS
 	bool "Bootdev support for Chromium OS"
-	depends on X86 || SANDBOX
-	default y
+	depends on X86 || ARM || SANDBOX
+	default y if !ARM
 	help
 	  Enables support for booting Chromium OS using bootdevs. This uses the
 	  kernel A slot and obtains the kernel command line from the parameters
diff --git a/boot/bootflow.c b/boot/bootflow.c
index 81b5829..6ef62e1 100644
--- a/boot/bootflow.c
+++ b/boot/bootflow.c
@@ -432,6 +432,7 @@
 	free(bflow->buf);
 	free(bflow->os_name);
 	free(bflow->fdt_fname);
+	free(bflow->bootmeth_priv);
 }
 
 void bootflow_remove(struct bootflow *bflow)
@@ -444,6 +445,22 @@
 	free(bflow);
 }
 
+#if CONFIG_IS_ENABLED(BOOTSTD_FULL)
+int bootflow_read_all(struct bootflow *bflow)
+{
+	int ret;
+
+	if (bflow->state != BOOTFLOWST_READY)
+		return log_msg_ret("rd", -EPROTO);
+
+	ret = bootmeth_read_all(bflow->method, bflow);
+	if (ret)
+		return log_msg_ret("rd2", ret);
+
+	return 0;
+}
+#endif /* BOOTSTD_FULL */
+
 int bootflow_boot(struct bootflow *bflow)
 {
 	int ret;
diff --git a/boot/bootm.c b/boot/bootm.c
index 75f0b4a..b1c3afe 100644
--- a/boot/bootm.c
+++ b/boot/bootm.c
@@ -823,6 +823,43 @@
 	return ret;
 }
 
+int bootm_boot_start(ulong addr, const char *cmdline)
+{
+	static struct cmd_tbl cmd = {"bootm"};
+	char addr_str[30];
+	char *argv[] = {addr_str, NULL};
+	int states;
+	int ret;
+
+	/*
+	 * TODO(sjg@chromium.org): This uses the command-line interface, but
+	 * should not. To clean this up, the various bootm states need to be
+	 * passed an info structure instead of cmdline flags. Then this can
+	 * set up the required info and move through the states without needing
+	 * the command line.
+	 */
+	states = BOOTM_STATE_START | BOOTM_STATE_FINDOS | BOOTM_STATE_PRE_LOAD |
+		BOOTM_STATE_FINDOTHER | BOOTM_STATE_LOADOS |
+		BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
+		BOOTM_STATE_OS_GO;
+	if (IS_ENABLED(CONFIG_SYS_BOOT_RAMDISK_HIGH))
+		states |= BOOTM_STATE_RAMDISK;
+	if (IS_ENABLED(CONFIG_PPC) || IS_ENABLED(CONFIG_MIPS))
+		states |= BOOTM_STATE_OS_CMDLINE;
+	images.state |= states;
+
+	snprintf(addr_str, sizeof(addr_str), "%lx", addr);
+
+	ret = env_set("bootargs", cmdline);
+	if (ret) {
+		printf("Failed to set cmdline\n");
+		return ret;
+	}
+	ret = do_bootm_states(&cmd, 0, 1, argv, states, &images, 1);
+
+	return ret;
+}
+
 #if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT)
 /**
  * image_get_kernel - verify legacy format kernel image
diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c
index 175eb1d..1d157d5 100644
--- a/boot/bootmeth-uclass.c
+++ b/boot/bootmeth-uclass.c
@@ -61,6 +61,18 @@
 	return ops->set_bootflow(dev, bflow, buf, size);
 }
 
+#if CONFIG_IS_ENABLED(BOOTSTD_FULL)
+int bootmeth_read_all(struct udevice *dev, struct bootflow *bflow)
+{
+	const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
+
+	if (!ops->read_all)
+		return -ENOSYS;
+
+	return ops->read_all(dev, bflow);
+}
+#endif /* BOOTSTD_FULL */
+
 int bootmeth_boot(struct udevice *dev, struct bootflow *bflow)
 {
 	const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
diff --git a/boot/bootmeth_cros.c b/boot/bootmeth_cros.c
index aa19ae0..1776fb1 100644
--- a/boot/bootmeth_cros.c
+++ b/boot/bootmeth_cros.c
@@ -12,24 +12,76 @@
 #include <blk.h>
 #include <bootdev.h>
 #include <bootflow.h>
+#include <bootm.h>
 #include <bootmeth.h>
+#include <display_options.h>
 #include <dm.h>
 #include <malloc.h>
 #include <mapmem.h>
 #include <part.h>
-#ifdef CONFIG_X86
-#include <asm/zimage.h>
-#endif
 #include <linux/sizes.h>
+#include "bootmeth_cros.h"
+
+/*
+ * Layout of the ChromeOS kernel
+ *
+ * Partitions 2 and 4 contain kernels
+ *
+ * Contents are:
+ *
+ * Offset	Contents
+ *   0		struct vb2_keyblock
+ *   m		struct vb2_kernel_preamble
+ *   m + n	kernel buffer
+ *
+ * m is keyblock->keyblock_size
+ * n is preamble->preamble_size
+ *
+ * The kernel buffer itself consists of various parts:
+ *
+ * Offset	Contents
+ *   m + n	kernel image (Flat vmlinux binary or FIT)
+ *   b - 8KB	Command line text
+ *   b - 4KB	X86 setup block (struct boot_params, extends for about 16KB)
+ *   b          X86 bootloader (continuation of setup block)
+ *   b + 16KB	X86 setup block (copy, used for hold data pointed to)
+ *
+ * b is m + n + preamble->bootloader_address - preamble->body_load_address
+ *
+ * Useful metadata extends from b - 8KB through to b + 32 KB
+ */
 
 enum {
-	/* Offsets in the kernel-partition header */
-	KERN_START	= 0x4f0,
-	KERN_SIZE	= 0x518,
+	PROBE_SIZE	= SZ_4K,	/* initial bytes read from partition */
 
-	SETUP_OFFSET	= 0x1000,	/* bytes before base */
-	CMDLINE_OFFSET	= 0x2000,	/* bytes before base */
-	OFFSET_BASE	= 0x100000,	/* assumed kernel load-address */
+	X86_SETUP_OFFSET = -0x1000,	/* setup offset relative to base */
+	CMDLINE_OFFSET	= -0x2000,	/* cmdline offset relative to base */
+	X86_KERNEL_OFFSET = 0x4000,	/* kernel offset relative to base */
+};
+
+/**
+ * struct cros_priv - Private data
+ *
+ * This is read from the disk and recorded for use when the full kernel must
+ * be loaded and booted
+ *
+ * @body_offset: Offset of kernel body from start of partition (in bytes)
+ * @body_size: Size of kernel body in bytes
+ * @part_start: Block offset of selected partition from the start of the disk
+ * @body_load_address: Nominal load address for kernel body
+ * @bootloader_address: Address of bootloader, after body is loaded at
+ *	body_load_address
+ * @bootloader_size:  Size of bootloader in bytes
+ * @info_buf: Buffer containing ChromiumOS info
+ */
+struct cros_priv {
+	ulong body_offset;
+	ulong body_size;
+	lbaint_t part_start;
+	ulong body_load_address;
+	ulong bootloader_address;
+	ulong bootloader_size;
+	void *info_buf;
 };
 
 static int cros_check(struct udevice *dev, struct bootflow_iter *iter)
@@ -77,61 +129,83 @@
 	return 0;
 }
 
-static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+/**
+ * scan_part() - Scan a kernel partition to see if has a ChromeOS header
+ *
+ * This reads the first PROBE_SIZE of a partition, loookng for
+ * VB2_KEYBLOCK_MAGIC
+ *
+ * @blk: Block device to scan
+ * @partnum: Partition number to scan
+ * @info: Please to put partition info
+ * @hdrp: Return allocated keyblock header on success
+ */
+static int scan_part(struct udevice *blk, int partnum,
+		     struct disk_partition *info, struct vb2_keyblock **hdrp)
 {
-	struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
-	ulong base, start, size, setup, cmdline, num_blks, kern_base;
-	struct disk_partition info;
-	const char *uuid = NULL;
-	void *buf, *hdr;
+	struct blk_desc *desc = dev_get_uclass_plat(blk);
+	struct vb2_keyblock *hdr;
+	ulong num_blks;
 	int ret;
 
-	log_debug("starting, part=%d\n", bflow->part);
-
-	/* We consider the whole disk, not any one partition */
-	if (bflow->part)
-		return log_msg_ret("max", -ENOENT);
-
-	/* Check partition 2 */
-	ret = part_get_info(desc, 2, &info);
+	ret = part_get_info(desc, partnum, info);
 	if (ret)
 		return log_msg_ret("part", ret);
 
 	/* Make a buffer for the header information */
-	num_blks = SZ_4K >> desc->log2blksz;
+	num_blks = PROBE_SIZE >> desc->log2blksz;
 	log_debug("Reading header, blk=%s, start=%lx, blocks=%lx\n",
-		  bflow->blk->name, (ulong)info.start, num_blks);
-	hdr = memalign(SZ_1K, SZ_4K);
+		  blk->name, (ulong)info->start, num_blks);
+	hdr = memalign(SZ_1K, PROBE_SIZE);
 	if (!hdr)
 		return log_msg_ret("hdr", -ENOMEM);
-	ret = blk_read(bflow->blk, info.start, num_blks, hdr);
-	if (ret != num_blks)
-		return log_msg_ret("inf", ret);
+	ret = blk_read(blk, info->start, num_blks, hdr);
+	if (ret != num_blks) {
+		free(hdr);
+		return log_msg_ret("inf", -EIO);
+	}
 
-	if (memcmp("CHROMEOS", hdr, 8))
+	if (memcmp(VB2_KEYBLOCK_MAGIC, hdr->magic, VB2_KEYBLOCK_MAGIC_SIZE)) {
+		free(hdr);
 		return -ENOENT;
+	}
 
-	log_info("Header at %lx\n", (ulong)map_to_sysmem(hdr));
-	start = *(u32 *)(hdr + KERN_START);
-	size = ALIGN(*(u32 *)(hdr + KERN_SIZE), desc->blksz);
-	log_debug("Reading start %lx size %lx\n", start, size);
-	bflow->size = size;
+	*hdrp = hdr;
 
-	buf = memalign(SZ_1K, size);
-	if (!buf)
-		return log_msg_ret("buf", -ENOMEM);
+	return 0;
+}
+
+/**
+ * cros_read_buf() - Read information into a buf and parse it
+ *
+ * @bflow: Bootflow to update
+ * @buf: Buffer to use
+ * @size: Size of buffer and number of bytes to read thereinto
+ * @start: Start offset to read from on disk
+ * @before_base: Number of bytes to read before the bootloader base
+ * @uuid: UUID string if supported, else NULL
+ * Return: 0 if OK, -ENOMEM if out of memory, -EIO on read failure
+ */
+static int cros_read_buf(struct bootflow *bflow, void *buf, ulong size,
+			 loff_t start, ulong before_base, const char *uuid)
+{
+	struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
+	ulong base, setup, cmdline, kern_base;
+	ulong num_blks;
+	int ret;
+
 	num_blks = size >> desc->log2blksz;
-	log_debug("Reading data, blk=%s, start=%lx, blocks=%lx\n",
-		  bflow->blk->name, (ulong)info.start, num_blks);
-	ret = blk_read(bflow->blk, (ulong)info.start + 0x80, num_blks, buf);
+	log_debug("Reading info to %lx, blk=%s, size=%lx, blocks=%lx\n",
+		  (ulong)map_to_sysmem(buf), bflow->blk->name, size, num_blks);
+	ret = blk_read(bflow->blk, start, num_blks, buf);
 	if (ret != num_blks)
-		return log_msg_ret("inf", ret);
-	base = map_to_sysmem(buf);
+		return log_msg_ret("inf", -EIO);
+	base = map_to_sysmem(buf) + before_base;
 
-	setup = base + start - OFFSET_BASE - SETUP_OFFSET;
-	cmdline = base + start - OFFSET_BASE - CMDLINE_OFFSET;
-	kern_base = base + start - OFFSET_BASE + SZ_16K;
-	log_debug("base %lx setup %lx, cmdline %lx, kern_base %lx\n", base,
+	setup = base + X86_SETUP_OFFSET;
+	cmdline = base + CMDLINE_OFFSET;
+	kern_base = base + X86_KERNEL_OFFSET;
+	log_debug("base %lx setup %lx cmdline %lx kern_base %lx\n", base,
 		  setup, cmdline, kern_base);
 
 #ifdef CONFIG_X86
@@ -151,35 +225,211 @@
 	if (!bflow->os_name)
 		return log_msg_ret("os", -ENOMEM);
 
-#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
-	uuid = info.uuid;
-#endif
 	ret = copy_cmdline(map_sysmem(cmdline, 0), uuid, &bflow->cmdline);
 	if (ret)
 		return log_msg_ret("cmd", ret);
+	bflow->x86_setup = map_sysmem(setup, 0);
 
-	bflow->state = BOOTFLOWST_READY;
+	return 0;
+}
+
+/**
+ * cros_read_info() - Read information and fill out the bootflow
+ *
+ * @bflow: Bootflow to update
+ * @uuid: UUID string if supported, else NULL
+ * @preamble: Kernel preamble information
+ * Return: 0 if OK, -ENOMEM if out of memory, -EIO on read failure
+ */
+static int cros_read_info(struct bootflow *bflow, const char *uuid,
+			  const struct vb2_kernel_preamble *preamble)
+{
+	struct cros_priv *priv = bflow->bootmeth_priv;
+	struct udevice *blk = bflow->blk;
+	struct blk_desc *desc = dev_get_uclass_plat(blk);
+	ulong offset, size, before_base;
+	void *buf;
+	int ret;
+
+	log_debug("Kernel preamble at %lx, version major %x, minor %x\n",
+		  (ulong)map_to_sysmem(preamble),
+		  preamble->header_version_major,
+		  preamble->header_version_minor);
+
+	log_debug("  - load_address %lx, bl_addr %lx, bl_size %lx\n",
+		  (ulong)preamble->body_load_address,
+		  (ulong)preamble->bootloader_address,
+		  (ulong)preamble->bootloader_size);
+
+	priv->body_size = preamble->body_signature.data_size;
+	priv->body_load_address = preamble->body_load_address;
+	priv->bootloader_address = preamble->bootloader_address;
+	priv->bootloader_size = preamble->bootloader_size;
+	log_debug("Kernel body at %lx size %lx\n", priv->body_offset,
+		  priv->body_size);
+
+	/* Work out how many bytes to read before the bootloader base */
+	before_base = -CMDLINE_OFFSET;
+
+	/* Read the cmdline through to the end of the bootloader */
+	size = priv->bootloader_size + before_base;
+	offset = priv->body_offset +
+		(priv->bootloader_address - priv->body_load_address) +
+		CMDLINE_OFFSET;
+	buf = malloc(size);
+	if (!buf)
+		return log_msg_ret("buf", -ENOMEM);
+
+	ret = cros_read_buf(bflow, buf, size,
+			    priv->part_start + (offset >> desc->log2blksz),
+			    before_base, uuid);
+	if (ret) {
+		/* Clear this since the buffer is invalid */
+		bflow->x86_setup = NULL;
+		free(buf);
+		return log_msg_ret("pro", ret);
+	}
+	priv->info_buf = buf;
+
+	return 0;
+}
+
+static int cros_read_kernel(struct bootflow *bflow)
+{
+	struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
+	struct cros_priv *priv = bflow->bootmeth_priv;
+	ulong base, setup;
+	ulong num_blks;
+	void *buf;
+	int ret;
+
+	bflow->size = priv->body_size;
+
+	buf = memalign(SZ_1K, priv->body_size);
+	if (!buf)
+		return log_msg_ret("buf", -ENOMEM);
+
+	/* Check that the header is not smaller than permitted */
+	if (priv->body_offset < PROBE_SIZE)
+		return log_msg_ret("san", EFAULT);
+
+	/* Read kernel body */
+	num_blks = priv->body_size >> desc->log2blksz;
+	log_debug("Reading body to %lx, blk=%s, size=%lx, blocks=%lx\n",
+		  (ulong)map_to_sysmem(buf), bflow->blk->name, priv->body_size,
+		  num_blks);
+	ret = blk_read(bflow->blk,
+		       priv->part_start + (priv->body_offset >> desc->log2blksz),
+		       num_blks, buf);
+	if (ret != num_blks)
+		return log_msg_ret("inf", -EIO);
+	base = map_to_sysmem(buf) + priv->bootloader_address -
+		priv->body_load_address;
+	setup = base + X86_SETUP_OFFSET;
+
 	bflow->buf = buf;
 	bflow->x86_setup = map_sysmem(setup, 0);
 
 	return 0;
 }
 
+static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+{
+	const struct vb2_kernel_preamble *preamble;
+	struct disk_partition info;
+	struct vb2_keyblock *hdr;
+	const char *uuid = NULL;
+	struct cros_priv *priv;
+	int part, ret;
+
+	log_debug("starting, part=%d\n", bflow->part);
+
+	/* We consider the whole disk, not any one partition */
+	if (bflow->part)
+		return log_msg_ret("max", -ENOENT);
+
+	/* Check partition 2 then 4 */
+	part = 2;
+	ret = scan_part(bflow->blk, part, &info, &hdr);
+	if (ret) {
+		part = 4;
+		ret = scan_part(bflow->blk, part, &info, &hdr);
+		if (ret)
+			return log_msg_ret("scan", ret);
+	}
+	bflow->part = part;
+
+	priv = malloc(sizeof(struct cros_priv));
+	if (!priv) {
+		free(hdr);
+		return log_msg_ret("buf", -ENOMEM);
+	}
+	bflow->bootmeth_priv = priv;
+
+	log_info("Selected partition %d, header at %lx\n", bflow->part,
+		 (ulong)map_to_sysmem(hdr));
+
+	/* Grab a few things from the preamble */
+	preamble = (void *)hdr + hdr->keyblock_size;
+	priv->body_offset = hdr->keyblock_size + preamble->preamble_size;
+	priv->part_start = info.start;
+
+	/* Now read everything we can learn about kernel */
+#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
+	uuid = info.uuid;
+#endif
+	ret = cros_read_info(bflow, uuid, preamble);
+	preamble = NULL;
+	free(hdr);
+	if (ret)
+		return log_msg_ret("inf", ret);
+	bflow->size = priv->body_size;
+	bflow->state = BOOTFLOWST_READY;
+
+	return 0;
+}
+
 static int cros_read_file(struct udevice *dev, struct bootflow *bflow,
 			 const char *file_path, ulong addr, ulong *sizep)
 {
 	return -ENOSYS;
 }
 
+#if CONFIG_IS_ENABLED(BOOSTD_FULL)
+static int cros_read_all(struct udevice *dev, struct bootflow *bflow)
+{
+	int ret;
+
+	if (bflow->buf)
+		return log_msg_ret("ld", -EALREADY);
+	ret = cros_read_kernel(bflow);
+	if (ret)
+		return log_msg_ret("rd", ret);
+
+	return 0;
+}
+#endif /* BOOSTD_FULL */
+
 static int cros_boot(struct udevice *dev, struct bootflow *bflow)
 {
-#ifdef CONFIG_X86
-	zboot_start(map_to_sysmem(bflow->buf), bflow->size, 0, 0,
-		    map_to_sysmem(bflow->x86_setup),
-		    bflow->cmdline);
-#endif
+	int ret;
 
-	return log_msg_ret("go", -EFAULT);
+	if (!bflow->buf) {
+		ret = cros_read_kernel(bflow);
+		if (ret)
+			return log_msg_ret("rd", ret);
+	}
+
+	if (IS_ENABLED(CONFIG_X86)) {
+		ret = zboot_start(map_to_sysmem(bflow->buf), bflow->size, 0, 0,
+				  map_to_sysmem(bflow->x86_setup),
+				  bflow->cmdline);
+	} else {
+		ret = bootm_boot_start(map_to_sysmem(bflow->buf),
+				       bflow->cmdline);
+	}
+
+	return log_msg_ret("go", ret);
 }
 
 static int cros_bootmeth_bind(struct udevice *dev)
@@ -196,6 +446,9 @@
 	.read_bootflow	= cros_read_bootflow,
 	.read_file	= cros_read_file,
 	.boot		= cros_boot,
+#if CONFIG_IS_ENABLED(BOOSTD_FULL)
+	.read_all	= cros_read_all,
+#endif /* BOOSTD_FULL */
 };
 
 static const struct udevice_id cros_bootmeth_ids[] = {
diff --git a/boot/bootmeth_cros.h b/boot/bootmeth_cros.h
new file mode 100644
index 0000000..8e30385
--- /dev/null
+++ b/boot/bootmeth_cros.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Structures used by the ChromiumOS bootmeth
+ *
+ * See docs at:
+ * https://www.chromium.org/chromium-os/chromiumos-design-docs/verified-boot-data-structures/
+ *
+ * Original code at:
+ * https://chromium.googlesource.com/chromiumos/platform/vboot_reference/+/refs/heads/main/firmware/2lib/include/2struct.h
+ *
+ * Code taken from vboot_reference commit 5b8596ce file 2struct.h
+ *
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef __BOOTMETH_CROS_H
+#define __BOOTMETH_CROS_H
+
+/* Signature data (a secure hash, possibly signed) */
+struct vb2_signature {
+	/* Offset of signature data from start of this struct */
+	uint32_t sig_offset;
+	uint32_t reserved0;
+
+	/* Size of signature data in bytes */
+	uint32_t sig_size;
+	uint32_t reserved1;
+
+	/* Size of the data block which was signed in bytes */
+	uint32_t data_size;
+	uint32_t reserved2;
+} __attribute__((packed));
+
+#define EXPECTED_VB2_SIGNATURE_SIZE 24
+
+/* Packed public key data */
+struct vb2_packed_key {
+	/* Offset of key data from start of this struct */
+	uint32_t key_offset;
+	uint32_t reserved0;
+
+	/* Size of key data in bytes (NOT strength of key in bits) */
+	uint32_t key_size;
+	uint32_t reserved1;
+
+	/* Signature algorithm used by the key (enum vb2_crypto_algorithm) */
+	uint32_t algorithm;
+	uint32_t reserved2;
+
+	/* Key version */
+	uint32_t key_version;
+	uint32_t reserved3;
+
+	/* TODO: when redoing this struct, add a text description of the key */
+} __attribute__((packed));
+
+#define EXPECTED_VB2_PACKED_KEY_SIZE 32
+
+#define VB2_KEYBLOCK_MAGIC "CHROMEOS"
+#define VB2_KEYBLOCK_MAGIC_SIZE 8
+
+/*
+ * Keyblock, containing the public key used to sign some other chunk of data.
+ *
+ * This should be followed by:
+ *   1) The data_key key data, pointed to by data_key.key_offset.
+ *   2) The checksum data for (vb2_keyblock + data_key data), pointed to
+ *      by keyblock_checksum.sig_offset.
+ *   3) The signature data for (vb2_keyblock + data_key data), pointed to
+ *      by keyblock_signature.sig_offset.
+ */
+struct vb2_keyblock {
+	/* Magic number */
+	uint8_t magic[VB2_KEYBLOCK_MAGIC_SIZE];
+
+	/* Version of this header format */
+	uint32_t header_version_major;
+	uint32_t header_version_minor;
+
+	/*
+	 * Length of this entire keyblock, including keys, signatures, and
+	 * padding, in bytes
+	 */
+	uint32_t keyblock_size;
+	uint32_t reserved0;
+
+	/*
+	 * Signature for this keyblock (header + data pointed to by data_key)
+	 * For use with signed data keys
+	 */
+	struct vb2_signature keyblock_signature;
+
+	/*
+	 * SHA-512 hash for this keyblock (header + data pointed to by
+	 * data_key) For use with unsigned data keys.
+	 *
+	 * Only supported for kernel keyblocks, not firmware keyblocks.
+	 */
+	struct vb2_signature keyblock_hash;
+
+	/* Flags for key (VB2_KEYBLOCK_FLAG_*) */
+	uint32_t keyblock_flags;
+	uint32_t reserved1;
+
+	/* Key to verify the chunk of data */
+	struct vb2_packed_key data_key;
+} __attribute__((packed));
+
+#define EXPECTED_VB2_KEYBLOCK_SIZE 112
+
+/*
+ * Preamble block for kernel, version 2.2
+ *
+ * This should be followed by:
+ *   1) The signature data for the kernel body, pointed to by
+ *      body_signature.sig_offset.
+ *   2) The signature data for (vb2_kernel_preamble + body signature data),
+ *       pointed to by preamble_signature.sig_offset.
+ *   3) The 16-bit vmlinuz header, which is used for reconstruction of
+ *      vmlinuz image.
+ */
+struct vb2_kernel_preamble {
+	/*
+	 * Size of this preamble, including keys, signatures, vmlinuz header,
+	 * and padding, in bytes
+	 */
+	uint32_t preamble_size;
+	uint32_t reserved0;
+
+	/* Signature for this preamble (header + body signature) */
+	struct vb2_signature preamble_signature;
+
+	/* Version of this header format */
+	uint32_t header_version_major;
+	uint32_t header_version_minor;
+
+	/* Kernel version */
+	uint32_t kernel_version;
+	uint32_t reserved1;
+
+	/* Load address for kernel body */
+	uint64_t body_load_address;
+	/* TODO (vboot 2.1): we never used that */
+
+	/* Address of bootloader, after body is loaded at body_load_address */
+	uint64_t bootloader_address;
+	/* TODO (vboot 2.1): should be a 32-bit offset */
+
+	/* Size of bootloader in bytes */
+	uint32_t bootloader_size;
+	uint32_t reserved2;
+
+	/* Signature for the kernel body */
+	struct vb2_signature body_signature;
+
+	/*
+	 * TODO (vboot 2.1): fields for kernel offset and size.  Right now the
+	 * size is implicitly the same as the size of data signed by the body
+	 * signature, and the offset is implicitly at the end of the preamble.
+	 * But that forces us to pad the preamble to 64KB rather than just
+	 * having a tiny preamble and an offset field.
+	 */
+
+	/*
+	 * Fields added in header version 2.1.  You must verify the header
+	 * version before reading these fields!
+	 */
+
+	/*
+	 * Address of 16-bit header for vmlinuz reassembly.  Readers should
+	 * return 0 for header version < 2.1.
+	 */
+	uint64_t vmlinuz_header_address;
+
+	/* Size of 16-bit header for vmlinuz in bytes.  Readers should return 0
+	   for header version < 2.1 */
+	uint32_t vmlinuz_header_size;
+	uint32_t reserved3;
+
+	/*
+	 * Fields added in header version 2.2.  You must verify the header
+	 * version before reading these fields!
+	 */
+
+	/*
+	 * Flags; see VB2_KERNEL_PREAMBLE_*.  Readers should return 0 for
+	 * header version < 2.2.  Flags field is currently defined as:
+	 * [31:2] - Reserved (for future use)
+	 * [1:0]  - Kernel image type (0b00 - CrOS,
+	 *                             0b01 - bootimg,
+	 *                             0b10 - multiboot)
+	 */
+	uint32_t flags;
+} __attribute__((packed));
+
+#endif /* __BOOTMETH_CROS_H */
diff --git a/cmd/bootflow.c b/cmd/bootflow.c
index c0aa4f8..3c3abaf 100644
--- a/cmd/bootflow.c
+++ b/cmd/bootflow.c
@@ -9,6 +9,7 @@
 #include <common.h>
 #include <bootdev.h>
 #include <bootflow.h>
+#include <bootm.h>
 #include <bootstd.h>
 #include <command.h>
 #include <console.h>
@@ -303,11 +304,14 @@
 {
 	struct bootstd_priv *std;
 	struct bootflow *bflow;
+	bool x86_setup = false;
 	bool dump = false;
 	int ret;
 
-	if (argc > 1 && *argv[1] == '-')
+	if (argc > 1 && *argv[1] == '-') {
 		dump = strchr(argv[1], 'd');
+		x86_setup = strchr(argv[1], 's');
+	}
 
 	ret = bootstd_get_priv(&std);
 	if (ret)
@@ -319,6 +323,12 @@
 	}
 	bflow = std->cur_bootflow;
 
+	if (IS_ENABLED(CONFIG_X86) && x86_setup) {
+		zimage_dump(bflow->x86_setup, false);
+
+		return 0;
+	}
+
 	printf("Name:      %s\n", bflow->name);
 	printf("Device:    %s\n", bflow->dev->name);
 	printf("Block dev: %s\n", bflow->blk ? bflow->blk->name : "(none)");
@@ -369,6 +379,35 @@
 	return 0;
 }
 
+static int do_bootflow_read(struct cmd_tbl *cmdtp, int flag, int argc,
+			    char *const argv[])
+{
+	struct bootstd_priv *std;
+	struct bootflow *bflow;
+	int ret;
+
+	ret = bootstd_get_priv(&std);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	/*
+	 * Require a current bootflow. Users can use 'bootflow scan -b' to
+	 * automatically scan and boot, if needed.
+	 */
+	if (!std->cur_bootflow) {
+		printf("No bootflow selected\n");
+		return CMD_RET_FAILURE;
+	}
+	bflow = std->cur_bootflow;
+	ret = bootflow_read_all(bflow);
+	if (ret) {
+		printf("Failed: err=%dE\n", ret);
+		return CMD_RET_FAILURE;
+	}
+
+	return 0;
+}
+
 static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc,
 			    char *const argv[])
 {
@@ -508,8 +547,9 @@
 	"scan [-abeGl] [bdev]  - scan for valid bootflows (-l list, -a all, -e errors, -b boot, -G no global)\n"
 	"bootflow list [-e]             - list scanned bootflows (-e errors)\n"
 	"bootflow select [<num>|<name>] - select a bootflow\n"
-	"bootflow info [-d]             - show info on current bootflow (-d dump bootflow)\n"
-	"bootflow boot                  - boot current bootflow (or first available if none selected)\n"
+	"bootflow info [-ds]            - show info on current bootflow (-d dump bootflow)\n"
+	"bootflow read                  - read all current-bootflow files\n"
+	"bootflow boot                  - boot current bootflow\n"
 	"bootflow menu [-t]             - show a menu of available bootflows\n"
 	"bootflow cmdline [set|get|clear|delete|auto] <param> [<value>] - update cmdline";
 #else
@@ -523,6 +563,7 @@
 	U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootflow_list),
 	U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootflow_select),
 	U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info),
+	U_BOOT_SUBCMD_MKENT(read, 1, 1, do_bootflow_read),
 	U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot),
 	U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu),
 	U_BOOT_SUBCMD_MKENT(cmdline, 4, 1, do_bootflow_cmdline),
diff --git a/doc/usage/cmd/bootflow.rst b/doc/usage/cmd/bootflow.rst
index a8af1f8..ead493d 100644
--- a/doc/usage/cmd/bootflow.rst
+++ b/doc/usage/cmd/bootflow.rst
@@ -11,7 +11,8 @@
     bootflow scan [-abelGH] [bootdev]
     bootflow list [-e]
     bootflow select [<num|name>]
-    bootflow info [-d]
+    bootflow info [-ds]
+    bootflow read
     bootflow boot
     bootflow cmdline [set|get|clear|delete|auto] <param> [<value>]
 
@@ -191,11 +192,29 @@
 
 Use the `-d` flag to dump out the contents of the bootfile file.
 
+The `-s` flag shows any x86 setup block, instead of the above.
+
+
+bootflow read
+~~~~~~~~~~~~~
+
+This reads any files related to the bootflow. Some bootflows with large files
+avoid doing this when the bootflow is scanned, since it uses a lot of memory
+and takes extra time. The files are then automatically read when `bootflow boot`
+is used.
+
+This command reads these files immediately. Typically this fills in the bootflow
+`buf` property, which can be used to examine the bootflow.
+
+Note that reading the files does not result in any extra parsing, nor loading of
+images in the files. This is purely used to read in the data ready for
+booting, or examination.
+
 
 bootflow boot
 ~~~~~~~~~~~~~
 
-This boots the current bootflow.
+This boots the current bootflow, reading any required files first.
 
 
 bootflow cmdline
@@ -522,6 +541,122 @@
     [    0.000000] Command line: loglevel=7 ... usb-storage.quirks=13fe:6500:u earlycon=uart8250,mmio32,0xfe03e000,115200n8
     [    0.000000] x86/split lock detection: warning about user-space split_locks
 
+This shows looking at x86 setup information::
+
+    => bootfl sel 0
+    => bootfl i -s
+    Setup located at 77b56010:
+
+    ACPI RSDP addr      : 0
+    E820: 2 entries
+            Addr        Size  Type
+               0        1000  RAM
+        fffff000        1000  Reserved
+    Setup sectors       : 1e
+    Root flags          : 1
+    Sys size            : 63420
+    RAM size            : 0
+    Video mode          : ffff
+    Root dev            : 0
+    Boot flag           : 0
+    Jump                : 66eb
+    Header              : 53726448
+                          Kernel V2
+    Version             : 20d
+    Real mode switch    : 0
+    Start sys seg       : 1000
+    Kernel version      : 38cc
+       @00003acc:
+    Type of loader      : ff
+                          unknown
+    Load flags          : 1
+                        : loaded-high
+    Setup move size     : 8000
+    Code32 start        : 100000
+    Ramdisk image       : 0
+    Ramdisk size        : 0
+    Bootsect kludge     : 0
+    Heap end ptr        : 5160
+    Ext loader ver      : 0
+    Ext loader type     : 0
+    Command line ptr    : 735000
+    Initrd addr max     : 7fffffff
+    Kernel alignment    : 200000
+    Relocatable kernel  : 1
+    Min alignment       : 15
+                        : 200000
+    Xload flags         : 3
+                        : 64-bit-entry can-load-above-4gb
+    Cmdline size        : 7ff
+    Hardware subarch    : 0
+    HW subarch data     : 0
+    Payload offset      : 26e
+    Payload length      : 612045
+    Setup data          : 0
+    Pref address        : 1000000
+    Init size           : 1383000
+    Handover offset     : 0
+
+This shows reading a bootflow to examine the kernel::
+
+    => bootfl i 0
+    Name:
+    Device:    emmc@1c,0.bootdev
+    Block dev: emmc@1c,0.blk
+    Method:    cros
+    State:     ready
+    Partition: 2
+    Subdir:    (none)
+    Filename:  <NULL>
+    Buffer:    0
+    Size:      63ee00 (6548992 bytes)
+    OS:        ChromeOS
+    Cmdline:   console= loglevel=7 init=/sbin/init cros_secure oops=panic panic=-1 root=PARTUUID=35c775e7-3735-d745-93e5-d9e0238f7ed0/PARTNROFF=1 rootwait rw dm_verity.error_behavior=3 dm_verity.max_bios=-1 dm_verity.dev_wait=0 dm="1 vroot none rw 1,0 3788800 verity payload=ROOT_DEV hashtree=HASH_DEV hashstart=3788800 alg=sha1 root_hexdigest=55052b629d3ac889f25a9583ea12cdcd3ea15ff8 salt=a2d4d9e574069f4fed5e3961b99054b7a4905414b60a25d89974a7334021165c" noinitrd vt.global_cursor_default=0 kern_guid=35c775e7-3735-d745-93e5-d9e0238f7ed0 add_efi_memmap boot=local noresume noswap i915.modeset=1 tpm_tis.force=1 tpm_tis.interrupts=0 nmi_watchdog=panic,lapic disablevmx=off
+    X86 setup: 77b56010
+    Logo:      (none)
+    FDT:       <NULL>
+    Error:     0
+
+Note that `Buffer` is 0 so it has not be read yet. Using `bootflow read`::
+
+    => bootfl read
+    => bootfl info
+    Name:
+    Device:    emmc@1c,0.bootdev
+    Block dev: emmc@1c,0.blk
+    Method:    cros
+    State:     ready
+    Partition: 2
+    Subdir:    (none)
+    Filename:  <NULL>
+    Buffer:    77b7e400
+    Size:      63ee00 (6548992 bytes)
+    OS:        ChromeOS
+    Cmdline:   console= loglevel=7 init=/sbin/init cros_secure oops=panic panic=-1 root=PARTUUID=35c775e7-3735-d745-93e5-d9e0238f7ed0/PARTNROFF=1 rootwait rw dm_verity.error_behavior=3 dm_verity.max_bios=-1 dm_verity.dev_wait=0 dm="1 vroot none rw 1,0 3788800 verity payload=ROOT_DEV hashtree=HASH_DEV hashstart=3788800 alg=sha1 root_hexdigest=55052b629d3ac889f25a9583ea12cdcd3ea15ff8 salt=a2d4d9e574069f4fed5e3961b99054b7a4905414b60a25d89974a7334021165c" noinitrd vt.global_cursor_default=0 kern_guid=35c775e7-3735-d745-93e5-d9e0238f7ed0 add_efi_memmap boot=local noresume noswap i915.modeset=1 tpm_tis.force=1 tpm_tis.interrupts=0 nmi_watchdog=panic,lapic disablevmx=off
+    X86 setup: 781b4400
+    Logo:      (none)
+    FDT:       <NULL>
+    Error:     0
+
+Now the buffer can be accessed::
+
+    => md 77b7e400
+    77b7e400: 1186f6fc 40000002 b8fa0c75 00000018  .......@u.......
+    77b7e410: c08ed88e a68dd08e 000001e8 000000e8  ................
+    77b7e420: ed815d00 00000021 62c280b8 89e80100  .]..!......b....
+    77b7e430: 22f7e8c4 c0850061 22ec850f eb890061  ..."a......"a...
+    77b7e440: 0230868b 01480000 21d0f7c3 00fb81c3  ..0...H....!....
+    77b7e450: 7d010000 0000bb05 c3810100 00d4f000  ...}............
+    77b7e460: 8130858d 85890061 00618132 3095010f  ..0.a...2.a....0
+    77b7e470: 0f006181 c883e020 e0220f20 e000bb8d  .a.. ... .".....
+    77b7e480: c0310062 001800b9 8dabf300 62e000bb  b.1............b
+    77b7e490: 07878d00 89000010 00bb8d07 8d0062f0  .............b..
+    77b7e4a0: 00100787 0004b900 07890000 00100005  ................
+    77b7e4b0: 08c78300 8df37549 630000bb 0183b800  ....Iu.....c....
+    77b7e4c0: 00b90000 89000008 00000507 c7830020  ............ ...
+    77b7e4d0: f3754908 e000838d 220f0062 0080b9d8  .Iu.....b.."....
+    77b7e4e0: 320fc000 08e8ba0f c031300f b8d0000f  ...2.....01.....
+    77b7e4f0: 00000020 6ad8000f 00858d10 50000002   ......j.......P
 
 
 Return value
diff --git a/include/bootflow.h b/include/bootflow.h
index 4152577..44d3741 100644
--- a/include/bootflow.h
+++ b/include/bootflow.h
@@ -83,6 +83,7 @@
  * @flags: Flags for the bootflow (see enum bootflow_flags_t)
  * @cmdline: OS command line, or NULL if not known (allocated)
  * @x86_setup: Pointer to x86 setup block inside @buf, NULL if not present
+ * @bootmeth_priv: Private data for the bootmeth
  */
 struct bootflow {
 	struct list_head bm_node;
@@ -107,7 +108,8 @@
 	ulong fdt_addr;
 	int flags;
 	char *cmdline;
-	char *x86_setup;
+	void *x86_setup;
+	void *bootmeth_priv;
 };
 
 /**
@@ -351,6 +353,17 @@
 int bootflow_boot(struct bootflow *bflow);
 
 /**
+ * bootflow_read_all() - Read all bootflow files
+ *
+ * Some bootmeths delay reading of large files until booting is requested. This
+ * causes those files to be read.
+ *
+ * @bflow: Bootflow to read
+ * Return: result of trying to read
+ */
+int bootflow_read_all(struct bootflow *bflow);
+
+/**
  * bootflow_run_boot() - Try to boot a bootflow
  *
  * @iter: Current iteration (or NULL if none). Used to disable a bootmeth if the
diff --git a/include/bootm.h b/include/bootm.h
index 044a479..c3c7336 100644
--- a/include/bootm.h
+++ b/include/bootm.h
@@ -9,6 +9,7 @@
 
 #include <image.h>
 
+struct boot_params;
 struct cmd_tbl;
 
 #define BOOTM_ERR_RESET		(-1)
@@ -124,4 +125,50 @@
  */
 int bootm_process_cmdline_env(int flags);
 
+/**
+ * zboot_start() - Boot a zimage
+ *
+ * Boot a zimage, given the component parts
+ *
+ * @addr: Address where the bzImage is moved before booting, either
+ *	BZIMAGE_LOAD_ADDR or ZIMAGE_LOAD_ADDR
+ * @base: Pointer to the boot parameters, typically at address
+ *	DEFAULT_SETUP_BASE
+ * @initrd: Address of the initial ramdisk, or 0 if none
+ * @initrd_size: Size of the initial ramdisk, or 0 if none
+ * @cmdline: Command line to use for booting
+ * Return: -EFAULT on error (normally it does not return)
+ */
+int zboot_start(ulong addr, ulong size, ulong initrd, ulong initrd_size,
+		ulong base, char *cmdline);
+
+/*
+ * zimage_get_kernel_version() - Get the version string from a kernel
+ *
+ * @params: boot_params pointer
+ * @kernel_base: base address of kernel
+ * Return: Kernel version as a NUL-terminated string
+ */
+const char *zimage_get_kernel_version(struct boot_params *params,
+				      void *kernel_base);
+
+/**
+ * zimage_dump() - Dump the metadata of a zimage
+ *
+ * This shows all available information in a zimage that has been loaded.
+ *
+ * @base_ptr: Pointer to the boot parameters, typically at address
+ *	DEFAULT_SETUP_BASE
+ * @show_cmdline: true to show the full command line
+ */
+void zimage_dump(struct boot_params *base_ptr, bool show_cmdline);
+
+/*
+ * bootm_boot_start() - Boot an image at the given address
+ *
+ * @addr: Image address
+ * @cmdline: Command line to set
+ */
+int bootm_boot_start(ulong addr, const char *cmdline);
+
 #endif
diff --git a/include/bootmeth.h b/include/bootmeth.h
index 7cb7da3..d3d8d60 100644
--- a/include/bootmeth.h
+++ b/include/bootmeth.h
@@ -119,7 +119,16 @@
 	 */
 	int (*read_file)(struct udevice *dev, struct bootflow *bflow,
 			 const char *file_path, ulong addr, ulong *sizep);
-
+#if CONFIG_IS_ENABLED(BOOTSTD_FULL)
+	/**
+	 * readall() - read all files for a bootflow
+	 *
+	 * @dev:	Bootmethod device to boot
+	 * @bflow:	Bootflow to read
+	 * Return: 0 if OK, -EIO on I/O error, other -ve on other error
+	 */
+	int (*read_all)(struct udevice *dev, struct bootflow *bflow);
+#endif /* BOOTSTD_FULL */
 	/**
 	 * boot() - boot a bootflow
 	 *
@@ -224,6 +233,20 @@
 		       const char *file_path, ulong addr, ulong *sizep);
 
 /**
+ * bootmeth_read_all() - read all bootflow files
+ *
+ * Some bootmeths delay reading of large files until booting is requested. This
+ * causes those files to be read.
+ *
+ * @dev:	Bootmethod device to use
+ * @bflow:	Bootflow to read
+ * Return: does not return on success, since it should boot the
+ *	Operating Systemn. Returns -EFAULT if that fails, other -ve on
+ *	other error
+ */
+int bootmeth_read_all(struct udevice *dev, struct bootflow *bflow);
+
+/**
  * bootmeth_boot() - boot a bootflow
  *
  * @dev:	Bootmethod device to boot