Merge https://source.denx.de/u-boot/custodians/u-boot-x86

- bootstd: Add a bootmeth for ChromiumOS on x86
- x86: Use qemu-x86_64 to boot EFI installers
diff --git a/arch/sandbox/include/asm/global_data.h b/arch/sandbox/include/asm/global_data.h
index f4ce72d..f0ab3ba 100644
--- a/arch/sandbox/include/asm/global_data.h
+++ b/arch/sandbox/include/asm/global_data.h
@@ -13,6 +13,10 @@
 struct arch_global_data {
 	uint8_t		*ram_buf;	/* emulated RAM buffer */
 	void		*text_base;	/* pointer to base of text region */
+	ulong table_start;		/* Start address of x86 tables */
+	ulong table_end;		/* End address of x86 tables */
+	ulong table_start_high;		/* Start address of high x86 tables */
+	ulong table_end_high;		/* End address of high x86 tables */
 };
 
 #include <asm-generic/global_data.h>
diff --git a/arch/x86/cpu/i386/interrupt.c b/arch/x86/cpu/i386/interrupt.c
index fae2544..f3f3527 100644
--- a/arch/x86/cpu/i386/interrupt.c
+++ b/arch/x86/cpu/i386/interrupt.c
@@ -266,6 +266,10 @@
 	struct udevice *dev;
 	int ret;
 
+	/*
+	 * When running as an EFI application we are not in control of
+	 * interrupts and should leave them alone.
+	 */
 	if (!ll_boot_init())
 		return 0;
 
@@ -274,11 +278,6 @@
 	if (ret && ret != -ENODEV)
 		return ret;
 
-	/*
-	 * When running as an EFI application we are not in control of
-	 * interrupts and should leave them alone.
-	 */
-#ifndef CONFIG_EFI_APP
 	/* Just in case... */
 	disable_interrupts();
 
@@ -294,14 +293,8 @@
 	/* Initialize core interrupt and exception functionality of CPU */
 	cpu_init_interrupts();
 
-	/*
-	 * It is now safe to enable interrupts.
-	 *
-	 * TODO(sjg@chromium.org): But we don't handle these correctly when
-	 * booted from EFI.
-	 */
+	/* It is now safe to enable interrupts */
 	enable_interrupts();
-#endif
 
 	return 0;
 }
diff --git a/arch/x86/cpu/intel_common/mrc.c b/arch/x86/cpu/intel_common/mrc.c
index 69405d7..56cc253 100644
--- a/arch/x86/cpu/intel_common/mrc.c
+++ b/arch/x86/cpu/intel_common/mrc.c
@@ -3,6 +3,8 @@
  * Copyright (c) 2016 Google, Inc
  */
 
+#define LOG_CATEGORY	UCLASS_RAM
+
 #include <common.h>
 #include <dm.h>
 #include <init.h>
@@ -144,12 +146,10 @@
 
 	ret = gpio_request_list_by_name(dev, "board-id-gpios", desc,
 					ARRAY_SIZE(desc), GPIOD_IS_IN);
-	if (ret < 0) {
-		debug("%s: gpio ret=%d\n", __func__, ret);
-		return ret;
-	}
+	if (ret < 0)
+		return log_msg_ret("gpio", ret);
 	spd_index = dm_gpio_get_values_as_int(desc, ret);
-	debug("spd index %d\n", spd_index);
+	log_debug("spd index %d\n", spd_index);
 
 	node = fdt_first_subnode(blob, dev_of_offset(dev));
 	if (node < 0)
diff --git a/arch/x86/cpu/ivybridge/sdram.c b/arch/x86/cpu/ivybridge/sdram.c
index 1a0ec43..0718aef 100644
--- a/arch/x86/cpu/ivybridge/sdram.c
+++ b/arch/x86/cpu/ivybridge/sdram.c
@@ -9,6 +9,8 @@
  * Copyright (C) 2011 Google Inc.
  */
 
+#define LOG_CATEGORY	UCLASS_RAM
+
 #include <common.h>
 #include <dm.h>
 #include <errno.h>
@@ -213,7 +215,7 @@
 
 	ret = mrc_locate_spd(dev, sizeof(peid->spd_data[0]), &data);
 	if (ret) {
-		debug("%s: Could not locate SPD (ret=%d)\n", __func__, ret);
+		log_debug("Could not locate SPD (err=%d)\n", ret);
 		return ret;
 	}
 
diff --git a/arch/x86/cpu/mtrr.c b/arch/x86/cpu/mtrr.c
index e69dfb5..d57fcfa 100644
--- a/arch/x86/cpu/mtrr.c
+++ b/arch/x86/cpu/mtrr.c
@@ -30,6 +30,16 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
+static const char *const mtrr_type_name[MTRR_TYPE_COUNT] = {
+	"Uncacheable",
+	"Combine",
+	"2",
+	"3",
+	"Through",
+	"Protect",
+	"Back",
+};
+
 /* Prepare to adjust MTRRs */
 void mtrr_open(struct mtrr_state *state, bool do_caches)
 {
@@ -320,3 +330,54 @@
 
 	return mtrr_start_op(cpu_select, &oper);
 }
+
+static void read_mtrrs_(void *arg)
+{
+	struct mtrr_info *info = arg;
+
+	mtrr_read_all(info);
+}
+
+int mtrr_list(int reg_count, int cpu_select)
+{
+	struct mtrr_info info;
+	int ret;
+	int i;
+
+	printf("Reg Valid Write-type   %-16s %-16s %-16s\n", "Base   ||",
+	       "Mask   ||", "Size   ||");
+	memset(&info, '\0', sizeof(info));
+	ret = mp_run_on_cpus(cpu_select, read_mtrrs_, &info);
+	if (ret)
+		return log_msg_ret("run", ret);
+	for (i = 0; i < reg_count; i++) {
+		const char *type = "Invalid";
+		u64 base, mask, size;
+		bool valid;
+
+		base = info.mtrr[i].base;
+		mask = info.mtrr[i].mask;
+		size = ~mask & ((1ULL << CONFIG_CPU_ADDR_BITS) - 1);
+		size |= (1 << 12) - 1;
+		size += 1;
+		valid = mask & MTRR_PHYS_MASK_VALID;
+		type = mtrr_type_name[base & MTRR_BASE_TYPE_MASK];
+		printf("%d   %-5s %-12s %016llx %016llx %016llx\n", i,
+		       valid ? "Y" : "N", type, base & ~MTRR_BASE_TYPE_MASK,
+		       mask & ~MTRR_PHYS_MASK_VALID, size);
+	}
+
+	return 0;
+}
+
+int mtrr_get_type_by_name(const char *typename)
+{
+	int i;
+
+	for (i = 0; i < MTRR_TYPE_COUNT; i++) {
+		if (*typename == *mtrr_type_name[i])
+			return i;
+	}
+
+	return -EINVAL;
+};
diff --git a/arch/x86/cpu/qemu/Kconfig b/arch/x86/cpu/qemu/Kconfig
index f8f2f64..aa329b0 100644
--- a/arch/x86/cpu/qemu/Kconfig
+++ b/arch/x86/cpu/qemu/Kconfig
@@ -12,7 +12,7 @@
 	imply SYS_NS16550
 	imply USB
 	imply USB_EHCI_HCD
-	imply VIDEO_VESA
+	imply VIDEO_BOCHS
 
 if QEMU
 
diff --git a/arch/x86/cpu/start64.S b/arch/x86/cpu/start64.S
index 7be8347..78e894d 100644
--- a/arch/x86/cpu/start64.S
+++ b/arch/x86/cpu/start64.S
@@ -26,3 +26,22 @@
 
 	/* Should not return here */
 	jmp	.
+
+.globl board_init_f_r_trampoline64
+.type board_init_f_r_trampoline64, @function
+board_init_f_r_trampoline64:
+	/*
+	 * SDRAM has been initialised, U-Boot code has been copied into
+	 * RAM, BSS has been cleared and relocation adjustments have been
+	 * made. It is now time to jump into the in-RAM copy of U-Boot
+	 *
+	 * %eax = Address of top of new stack
+	 */
+
+	/* Stack grows down from top of SDRAM */
+	movq	%rsi, %rsp
+
+	/* New gd is in rdi */
+
+	/* Re-enter U-Boot by calling board_init_f_r() */
+	call	board_init_f_r
diff --git a/arch/x86/dts/chromebook_link.dts b/arch/x86/dts/chromebook_link.dts
index 36956f4..c904b7d 100644
--- a/arch/x86/dts/chromebook_link.dts
+++ b/arch/x86/dts/chromebook_link.dts
@@ -314,6 +314,7 @@
 						00 00 00 00 00 00 00 00];
 					};
 				micron_4Gb_1600_1.35v_x16 {
+					bootph-all;
 					reg = <2>;
 					data = [92 11 0b 03 04 19 02 02
 						03 11 01 08 0a 00 fe 00
diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h
index 22d103d..ea58259 100644
--- a/arch/x86/include/asm/global_data.h
+++ b/arch/x86/include/asm/global_data.h
@@ -123,6 +123,10 @@
 #endif
 	void *itss_priv;		/* Private ITSS data pointer */
 	ulong coreboot_table;		/* Address of coreboot table */
+	ulong table_start;		/* Start address of x86 tables */
+	ulong table_end;		/* End address of x86 tables */
+	ulong table_start_high;		/* Start address of high x86 tables */
+	ulong table_end_high;		/* End address of high x86 tables */
 };
 
 #endif
diff --git a/arch/x86/include/asm/mtrr.h b/arch/x86/include/asm/mtrr.h
index ca2edc7..2e995f5 100644
--- a/arch/x86/include/asm/mtrr.h
+++ b/arch/x86/include/asm/mtrr.h
@@ -190,6 +190,26 @@
  */
 int mtrr_get_var_count(void);
 
+/**
+ * mtrr_list() - List the MTRRs
+ *
+ * Shows a list of all the MTRRs including their values
+ *
+ * @reg_count: Number of registers to show. You can use mtrr_get_var_count() for
+ * this
+ * @cpu_select: CPU to use. Use MP_SELECT_BSP for the boot CPU
+ * Returns: 0 if OK, -ve if the CPU was not found
+ */
+int mtrr_list(int reg_count, int cpu_select);
+
+/**
+ * mtrr_get_type_by_name() - Get the type of an MTRR given its type name
+ *
+ * @typename: Name to check
+ * Returns: MTRR type (MTRR_TYPE_...) or -EINVAL if invalid
+ */
+int mtrr_get_type_by_name(const char *typename);
+
 #endif
 
 #if ((CONFIG_XIP_ROM_SIZE & (CONFIG_XIP_ROM_SIZE - 1)) != 0)
diff --git a/arch/x86/include/asm/u-boot-x86.h b/arch/x86/include/asm/u-boot-x86.h
index 8f38c2d..02a8b0f 100644
--- a/arch/x86/include/asm/u-boot-x86.h
+++ b/arch/x86/include/asm/u-boot-x86.h
@@ -102,8 +102,31 @@
  */
 int fsp_save_s3_stack(void);
 
-void	board_init_f_r_trampoline(ulong) __attribute__ ((noreturn));
-void	board_init_f_r(void) __attribute__ ((noreturn));
+/**
+ * board_init_f_r_trampoline() - jump to relocated address with new stack
+ *
+ * @sp: New stack pointer to use
+ */
+void __noreturn board_init_f_r_trampoline(ulong sp);
+
+/**
+ * board_init_f_r() - jump to relocated U-Boot
+ *
+ * This is used to jump from pre-relocation to post-relocation U-Boot. It
+ * enables the cache and jump to the new location.
+ */
+void __noreturn board_init_f_r(void);
+
+/*
+ * board_init_f_r_trampoline64() - jump to relocated address with new stack
+ *
+ * This is the 64-bit version
+ *
+ * @new_gd: New global_data pointer to use
+ * @sp: New stack pointer to pass on to board_init_r()
+ */
+void __noreturn board_init_f_r_trampoline64(struct global_data *new_gd,
+					    ulong sp);
 
 int arch_misc_init(void);
 
diff --git a/arch/x86/include/asm/zimage.h b/arch/x86/include/asm/zimage.h
index 000b38e..9ad74dc 100644
--- a/arch/x86/include/asm/zimage.h
+++ b/arch/x86/include/asm/zimage.h
@@ -72,4 +72,31 @@
  */
 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/Makefile b/arch/x86/lib/Makefile
index b0612ae..90a7618 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -4,16 +4,17 @@
 # Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 
 obj-y	+= bdinfo.o
-ifndef CONFIG_X86_64
-ifndef CONFIG_TPL_BUILD
+
+ifndef CONFIG_$(SPL_TPL_)X86_64
 obj-y += bios.o
 obj-y += bios_asm.o
 obj-y += bios_interrupts.o
 endif
-endif
+
 ifndef CONFIG_SPL_BUILD
 obj-$(CONFIG_X86_32BIT_INIT) += string.o
 endif
+
 ifndef CONFIG_SPL_BUILD
 obj-$(CONFIG_CMD_BOOTM) += bootm.o
 endif
diff --git a/arch/x86/lib/bdinfo.c b/arch/x86/lib/bdinfo.c
index 1539007..1240584 100644
--- a/arch/x86/lib/bdinfo.c
+++ b/arch/x86/lib/bdinfo.c
@@ -22,6 +22,11 @@
 	bdinfo_print_num_l("vendor", gd->arch.x86_vendor);
 	bdinfo_print_str(" name", cpu_vendor_name(gd->arch.x86_vendor));
 	bdinfo_print_num_l("model", gd->arch.x86_model);
+	bdinfo_print_num_l("phys_addr in bits", cpu_phys_address_size());
+	bdinfo_print_num_l("table start", gd->arch.table_start);
+	bdinfo_print_num_l("table end", gd->arch.table_end);
+	bdinfo_print_num_l(" high start", gd->arch.table_start_high);
+	bdinfo_print_num_l(" high end", gd->arch.table_end_high);
 
 	if (IS_ENABLED(CONFIG_EFI_STUB))
 		efi_show_bdinfo();
diff --git a/arch/x86/lib/bios.c b/arch/x86/lib/bios.c
index 94349ba..e29cae7 100644
--- a/arch/x86/lib/bios.c
+++ b/arch/x86/lib/bios.c
@@ -23,7 +23,7 @@
 static int (*int_handler[256])(void);
 
 /* to have a common register file for interrupt handlers */
-#ifndef CONFIG_BIOSEMU
+#if !CONFIG_IS_ENABLED(BIOSEMU)
 X86EMU_sysEnv _X86EMU_env;
 #endif
 
@@ -78,7 +78,7 @@
 	};
 	struct eregs *regs = &reg_info;
 
-	debug("Oops, exception %d while executing option rom\n", regs->vector);
+	log_err("Exception %d while executing option rom\n", regs->vector);
 	cpu_hlt();
 
 	return 0;
diff --git a/arch/x86/lib/bootm.c b/arch/x86/lib/bootm.c
index 61cb7bc..3196f9d 100644
--- a/arch/x86/lib/bootm.c
+++ b/arch/x86/lib/bootm.c
@@ -258,7 +258,7 @@
 	ulong ret;
 
 #if CONFIG_IS_ENABLED(X86_64)
-	ret = gd->start_addr_sp;
+	asm("mov %%rsp, %0" : "=r"(ret) : );
 #else
 	asm("mov %%esp, %0" : "=r"(ret) : );
 #endif
diff --git a/arch/x86/lib/mrccache.c b/arch/x86/lib/mrccache.c
index 2f6f688..6494b8d 100644
--- a/arch/x86/lib/mrccache.c
+++ b/arch/x86/lib/mrccache.c
@@ -6,6 +6,8 @@
  * Copyright (C) 2015 Bin Meng <bmeng.cn@gmail.com>
  */
 
+#define LOG_CATEGORY	UCLASS_RAM
+
 #include <common.h>
 #include <dm.h>
 #include <errno.h>
@@ -197,8 +199,8 @@
 	cache->signature = MRC_DATA_SIGNATURE;
 	cache->data_size = mrc->len;
 	checksum = compute_ip_checksum(mrc->buf, cache->data_size);
-	debug("Saving %d bytes for MRC output data, checksum %04x\n",
-	      cache->data_size, checksum);
+	log_debug("Saving %d bytes for MRC output data, checksum %04x\n",
+		  cache->data_size, checksum);
 	cache->checksum = checksum;
 	cache->reserved = 0;
 	memcpy(cache->data, mrc->buf, cache->data_size);
diff --git a/arch/x86/lib/spl.c b/arch/x86/lib/spl.c
index ca1645f..b6812bb 100644
--- a/arch/x86/lib/spl.c
+++ b/arch/x86/lib/spl.c
@@ -3,6 +3,8 @@
  * Copyright (c) 2016 Google, Inc
  */
 
+#define LOG_CATEGORY	LOGC_BOOT
+
 #include <common.h>
 #include <cpu_func.h>
 #include <debug_uart.h>
@@ -15,10 +17,12 @@
 #include <malloc.h>
 #include <spl.h>
 #include <syscon.h>
+#include <vesa.h>
 #include <asm/cpu.h>
 #include <asm/cpu_common.h>
 #include <asm/fsp2/fsp_api.h>
 #include <asm/global_data.h>
+#include <asm/mp.h>
 #include <asm/mrccache.h>
 #include <asm/mtrr.h>
 #include <asm/pci.h>
@@ -61,6 +65,8 @@
 
 static int x86_spl_init(void)
 {
+	struct udevice *dev;
+
 #ifndef CONFIG_TPL
 	/*
 	 * TODO(sjg@chromium.org): We use this area of RAM for the stack
@@ -74,49 +80,64 @@
 #endif
 	int ret;
 
-	debug("%s starting\n", __func__);
+	log_debug("x86 spl starting\n");
 	if (IS_ENABLED(TPL))
 		ret = x86_cpu_reinit_f();
 	else
 		ret = x86_cpu_init_f();
 	ret = spl_init();
 	if (ret) {
-		debug("%s: spl_init() failed\n", __func__);
+		log_debug("spl_init() failed (err=%d)\n", ret);
 		return ret;
 	}
 	ret = arch_cpu_init();
 	if (ret) {
-		debug("%s: arch_cpu_init() failed\n", __func__);
+		log_debug("arch_cpu_init() failed (err=%d)\n", ret);
 		return ret;
 	}
 #ifndef CONFIG_TPL
 	ret = fsp_setup_pinctrl(NULL, NULL);
 	if (ret) {
-		debug("%s: fsp_setup_pinctrl() failed\n", __func__);
+		log_debug("fsp_setup_pinctrl() failed (err=%d)\n", ret);
 		return ret;
 	}
 #endif
-	preloader_console_init();
+	/*
+	 * spl_board_init() below sets up the console if enabled. If it isn't,
+	 * do it here. We cannot call this twice since it results in a double
+	 * banner and CI tests fail.
+	 */
+	if (!IS_ENABLED(CONFIG_SPL_BOARD_INIT))
+		preloader_console_init();
 #if !defined(CONFIG_TPL) && !CONFIG_IS_ENABLED(CPU)
 	ret = print_cpuinfo();
 	if (ret) {
-		debug("%s: print_cpuinfo() failed\n", __func__);
+		log_debug("print_cpuinfo() failed (err=%d)\n", ret);
 		return ret;
 	}
 #endif
+	/* probe the LPC so we get the GPIO_BASE set up correctly */
+	ret = uclass_first_device_err(UCLASS_LPC, &dev);
+	if (ret && ret != -ENODEV) {
+		log_debug("lpc probe failed\n");
+		return ret;
+	}
+
 	ret = dram_init();
 	if (ret) {
-		debug("%s: dram_init() failed\n", __func__);
+		log_debug("dram_init() failed (err=%d)\n", ret);
 		return ret;
 	}
+	log_debug("mrc\n");
 	if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE)) {
 		ret = mrccache_spl_save();
 		if (ret)
-			debug("%s: Failed to write to mrccache (err=%d)\n",
-			      __func__, ret);
+			log_debug("Failed to write to mrccache (err=%d)\n",
+				  ret);
 	}
 
 #ifndef CONFIG_SYS_COREBOOT
+	log_debug("bss\n");
 	debug("BSS clear from %lx to %lx len %lx\n", (ulong)&__bss_start,
 	      (ulong)&__bss_end, (ulong)&__bss_end - (ulong)&__bss_start);
 	memset(&__bss_start, 0, (ulong)&__bss_end - (ulong)&__bss_start);
@@ -136,9 +157,29 @@
 	 */
 	gd->new_gd = (struct global_data *)ptr;
 	memcpy(gd->new_gd, gd, sizeof(*gd));
+
+	log_debug("logging\n");
+	/*
+	 * Make sure logging is disabled when we switch, since the log system
+	 * list head will move
+	 */
+	gd->new_gd->flags &= ~GD_FLG_LOG_READY;
 	arch_setup_gd(gd->new_gd);
 	gd->start_addr_sp = (ulong)ptr;
 
+	/* start up logging again, with the new list-head location */
+	ret = log_init();
+	if (ret) {
+		log_debug("Log setup failed (err=%d)\n", ret);
+		return ret;
+	}
+
+	if (_LOG_DEBUG) {
+		ret = mtrr_list(mtrr_get_var_count(), MP_SELECT_BSP);
+		if (ret)
+			printf("mtrr_list failed\n");
+	}
+
 	/* Cache the SPI flash. Otherwise copying the code to RAM takes ages */
 	ret = mtrr_add_request(MTRR_TYPE_WRBACK,
 			       (1ULL << 32) - CONFIG_XIP_ROM_SIZE,
@@ -157,6 +198,7 @@
 		debug("Failed to set CPU frequency (err=%d)\n", ret);
 # endif
 #endif
+	log_debug("done\n");
 
 	return 0;
 }
@@ -250,4 +292,12 @@
 #ifndef CONFIG_TPL
 	preloader_console_init();
 #endif
+
+	if (CONFIG_IS_ENABLED(VIDEO)) {
+		struct udevice *dev;
+
+		/* Set up PCI video in SPL if required */
+		uclass_first_device_err(UCLASS_PCI, &dev);
+		uclass_first_device_err(UCLASS_VIDEO, &dev);
+	}
 }
diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c
index ea834a5..67bc0a7 100644
--- a/arch/x86/lib/tables.c
+++ b/arch/x86/lib/tables.c
@@ -3,7 +3,7 @@
  * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
  */
 
-#define LOG_CATEGORY LOGC_BOARD
+#define LOG_CATEGORY LOGC_ACPI
 
 #include <common.h>
 #include <bloblist.h>
@@ -54,6 +54,10 @@
 #ifdef CONFIG_GENERATE_MP_TABLE
 	{ "mp", write_mp_table, },
 #endif
+	/*
+	 * tables which can go in the bloblist must be last in this list, so
+	 * that the calculation of gd->table_end works properly
+	 */
 #ifdef CONFIG_GENERATE_ACPI_TABLE
 	{ "acpi", write_acpi_tables, BLOBLISTT_ACPI_TABLES, 0x10000, 0x1000},
 #endif
@@ -78,33 +82,42 @@
 
 int write_tables(void)
 {
-	u32 rom_table_start;
-	u32 rom_table_end;
 	u32 high_table, table_size;
 	struct memory_area cfg_tables[ARRAY_SIZE(table_list) + 1];
+	bool use_high = false;
+	u32 rom_addr;
 	int i;
 
-	rom_table_start = ROM_TABLE_ADDR;
+	gd->arch.table_start = ROM_TABLE_ADDR;
+	rom_addr = gd->arch.table_start;
 
-	debug("Writing tables to %x:\n", rom_table_start);
+	debug("Writing tables to %x:\n", rom_addr);
 	for (i = 0; i < ARRAY_SIZE(table_list); i++) {
 		const struct table_info *table = &table_list[i];
 		int size = table->size ? : CONFIG_ROM_TABLE_SIZE;
+		u32 rom_table_end;
 
 		if (IS_ENABLED(CONFIG_BLOBLIST_TABLES) && table->tag) {
-			rom_table_start = (ulong)bloblist_add(table->tag, size,
-							      table->align);
-			if (!rom_table_start)
+			if (!gd->arch.table_end)
+				gd->arch.table_end = rom_addr;
+			rom_addr = (ulong)bloblist_add(table->tag, size,
+						       table->align);
+			if (!rom_addr)
 				return log_msg_ret("bloblist", -ENOBUFS);
+
+			/* the bloblist is always in high memory */
+			use_high = true;
+			if (!gd->arch.table_start_high)
+				gd->arch.table_start_high = rom_addr;
 		}
-		rom_table_end = table->write(rom_table_start);
+		rom_table_end = table->write(rom_addr);
 		if (!rom_table_end) {
 			log_err("Can't create configuration table %d\n", i);
 			return -EINTR;
 		}
 
 		if (IS_ENABLED(CONFIG_SEABIOS)) {
-			table_size = rom_table_end - rom_table_start;
+			table_size = rom_table_end - rom_addr;
 			high_table = (u32)(ulong)high_table_malloc(table_size);
 			if (high_table) {
 				if (!table->write(high_table)) {
@@ -123,15 +136,20 @@
 		}
 
 		debug("- wrote '%s' to %x, end %x\n", table->name,
-		      rom_table_start, rom_table_end);
-		if (rom_table_end - rom_table_start > size) {
+		      rom_addr, rom_table_end);
+		if (rom_table_end - rom_addr > size) {
 			log_err("Out of space for configuration tables: need %x, have %x\n",
-				rom_table_end - rom_table_start, size);
+				rom_table_end - rom_addr, size);
 			return log_msg_ret("bloblist", -ENOSPC);
 		}
-		rom_table_start = rom_table_end;
+		rom_addr = rom_table_end;
 	}
 
+	if (use_high)
+		gd->arch.table_end_high = rom_addr;
+	else
+		gd->arch.table_end = rom_addr;
+
 	if (IS_ENABLED(CONFIG_SEABIOS)) {
 		/* make sure the last item is zero */
 		cfg_tables[i].size = 0;
diff --git a/arch/x86/lib/zimage.c b/arch/x86/lib/zimage.c
index e5ea512..062e3d3 100644
--- a/arch/x86/lib/zimage.c
+++ b/arch/x86/lib/zimage.c
@@ -22,6 +22,7 @@
 #include <irq_func.h>
 #include <log.h>
 #include <malloc.h>
+#include <mapmem.h>
 #include <acpi/acpi_table.h>
 #include <asm/io.h>
 #include <asm/ptrace.h>
@@ -180,7 +181,7 @@
 	return 0;
 }
 
-static const char *get_kernel_version(struct boot_params *params,
+const char *zimage_get_kernel_version(struct boot_params *params,
 				      void *kernel_base)
 {
 	struct setup_header *hdr = &params->hdr;
@@ -188,10 +189,14 @@
 	const char *s, *end;
 
 	bootproto = get_boot_protocol(hdr, false);
+	log_debug("bootproto %x, hdr->setup_sects %x\n", bootproto,
+		  hdr->setup_sects);
 	if (bootproto < 0x0200 || hdr->setup_sects < 15)
 		return NULL;
 
 	/* sanity-check the kernel version in case it is missing */
+	log_debug("hdr->kernel_version %x, str at %p\n", hdr->kernel_version,
+		  kernel_base + hdr->kernel_version + 0x200);
 	for (s = kernel_base + hdr->kernel_version + 0x200, end = s + 0x100; *s;
 	     s++) {
 		if (!isprint(*s))
@@ -238,7 +243,7 @@
 	log_debug("Using boot protocol version %x.%02x\n",
 		  (bootproto & 0xff00) >> 8, bootproto & 0xff);
 
-	version = get_kernel_version(params, image);
+	version = zimage_get_kernel_version(params, image);
 	if (version)
 		printf("Linux kernel version %s\n", version);
 	else
@@ -442,8 +447,7 @@
 	return 0;
 }
 
-static int do_zboot_load(struct cmd_tbl *cmdtp, int flag, int argc,
-			 char *const argv[])
+static int zboot_load(void)
 {
 	struct boot_params *base_ptr;
 
@@ -460,31 +464,51 @@
 				       &state.load_address);
 		if (!base_ptr) {
 			puts("## Kernel loading failed ...\n");
-			return CMD_RET_FAILURE;
+			return -EINVAL;
 		}
 	}
 	state.base_ptr = base_ptr;
-	if (env_set_hex("zbootbase", (ulong)base_ptr) ||
+
+	return 0;
+}
+
+static int do_zboot_load(struct cmd_tbl *cmdtp, int flag, int argc,
+			 char *const argv[])
+{
+	if (zboot_load())
+		return CMD_RET_FAILURE;
+
+	if (env_set_hex("zbootbase", map_to_sysmem(state.base_ptr)) ||
 	    env_set_hex("zbootaddr", state.load_address))
 		return CMD_RET_FAILURE;
 
 	return 0;
 }
 
+static int zboot_setup(void)
+{
+	struct boot_params *base_ptr = state.base_ptr;
+	int ret;
+
+	ret = setup_zimage(base_ptr, (char *)base_ptr + COMMAND_LINE_OFFSET,
+			   0, state.initrd_addr, state.initrd_size,
+			   (ulong)state.cmdline);
+	if (ret)
+		return -EINVAL;
+
+	return 0;
+}
+
 static int do_zboot_setup(struct cmd_tbl *cmdtp, int flag, int argc,
 			  char *const argv[])
 {
 	struct boot_params *base_ptr = state.base_ptr;
-	int ret;
 
 	if (!base_ptr) {
 		printf("base is not set: use 'zboot load' first\n");
 		return CMD_RET_FAILURE;
 	}
-	ret = setup_zimage(base_ptr, (char *)base_ptr + COMMAND_LINE_OFFSET,
-			   0, state.initrd_addr, state.initrd_size,
-			   (ulong)state.cmdline);
-	if (ret) {
+	if (zboot_setup()) {
 		puts("Setting up boot parameters failed ...\n");
 		return CMD_RET_FAILURE;
 	}
@@ -501,8 +525,7 @@
 	return 0;
 }
 
-static int do_zboot_go(struct cmd_tbl *cmdtp, int flag, int argc,
-		       char *const argv[])
+static int zboot_go(void)
 {
 	struct boot_params *params = state.base_ptr;
 	struct setup_header *hdr = &params->hdr;
@@ -522,11 +545,52 @@
 
 	/* we assume that the kernel is in place */
 	ret = boot_linux_kernel((ulong)state.base_ptr, entry, image_64bit);
+
+	return ret;
+}
+
+static int do_zboot_go(struct cmd_tbl *cmdtp, int flag, int argc,
+		       char *const argv[])
+{
+	int ret;
+
+	ret = zboot_go();
 	printf("Kernel returned! (err=%d)\n", ret);
 
 	return CMD_RET_FAILURE;
 }
 
+int zboot_start(ulong addr, ulong size, ulong initrd, ulong initrd_size,
+		ulong base, char *cmdline)
+{
+	int ret;
+
+	memset(&state, '\0', sizeof(state));
+
+	if (base) {
+		state.base_ptr = map_sysmem(base, 0);
+		state.load_address = addr;
+	} else {
+		state.bzimage_addr = addr;
+	}
+	state.bzimage_size = size;
+	state.initrd_addr = initrd;
+	state.initrd_size = initrd_size;
+	state.cmdline = cmdline;
+
+	ret = zboot_load();
+	if (ret)
+		return log_msg_ret("ld", ret);
+	ret = zboot_setup();
+	if (ret)
+		return log_msg_ret("set", ret);
+	ret = zboot_go();
+	if (ret)
+		return log_msg_ret("set", ret);
+
+	return -EFAULT;
+}
+
 static void print_num(const char *name, ulong value)
 {
 	printf("%-20s: %lx\n", name, value);
@@ -668,7 +732,8 @@
 	print_num("Real mode switch", hdr->realmode_swtch);
 	print_num("Start sys seg", hdr->start_sys_seg);
 	print_num("Kernel version", hdr->kernel_version);
-	version = get_kernel_version(base_ptr, (void *)state.bzimage_addr);
+	version = zimage_get_kernel_version(base_ptr,
+					    (void *)state.bzimage_addr);
 	if (version)
 		printf("   @%p: %s\n", version, version);
 	print_num("Type of loader", hdr->type_of_loader);
diff --git a/board/google/Kconfig b/board/google/Kconfig
index a0f1a60..e4f9b5b 100644
--- a/board/google/Kconfig
+++ b/board/google/Kconfig
@@ -4,13 +4,6 @@
 
 if VENDOR_GOOGLE
 
-config BIOSEMU
-	bool
-	select X86EMU_RAW_IO
-
-config X86EMU_RAW_IO
-	bool
-
 choice
 	prompt "Mainboard model"
 	optional
diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c
index c7b6cb7..9d58860 100644
--- a/board/sandbox/sandbox.c
+++ b/board/sandbox/sandbox.c
@@ -11,16 +11,18 @@
 #include <efi.h>
 #include <efi_loader.h>
 #include <env_internal.h>
+#include <extension_board.h>
 #include <init.h>
 #include <led.h>
+#include <malloc.h>
+#include <mapmem.h>
 #include <os.h>
+#include <acpi/acpi_table.h>
 #include <asm/global_data.h>
 #include <asm/test.h>
 #include <asm/u-boot-sandbox.h>
 #include <linux/kernel.h>
-#include <malloc.h>
-
-#include <extension_board.h>
+#include <linux/sizes.h>
 
 /*
  * Pointer to initial global data area
@@ -154,6 +156,8 @@
 int board_late_init(void)
 {
 	struct udevice *dev;
+	ulong addr, end;
+	void *ptr;
 	int ret;
 
 	ret = uclass_first_device_err(UCLASS_CROS_EC, &dev);
@@ -166,6 +170,18 @@
 		panic("Cannot init cros-ec device");
 		return -1;
 	}
+
+	if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) {
+		/* Reserve 64K for ACPI tables, aligned to a 4K boundary */
+		ptr = memalign(SZ_4K, SZ_64K);
+		addr = map_to_sysmem(ptr);
+
+		/* Generate ACPI tables */
+		end = write_acpi_tables(addr);
+		gd->arch.table_start = addr;
+		gd->arch.table_end = addr;
+	}
+
 	return 0;
 }
 #endif
diff --git a/boot/Kconfig b/boot/Kconfig
index c8b8f36..b424265 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -463,6 +463,17 @@
 	  EFI bootmgr, since they take full control over which bootdevs are
 	  selected to boot.
 
+config BOOTMETH_CROS
+	bool "Bootdev support for Chromium OS"
+	depends on X86 || SANDBOX
+	default y
+	help
+	  Enables support for booting Chromium OS using bootdevs. This uses the
+	  kernel A slot and obtains the kernel command line from the parameters
+	  provided there.
+
+	  Note that only x86 devices are supported at present.
+
 config BOOTMETH_EXTLINUX
 	bool "Bootdev support for extlinux boot"
 	select PXE_UTILS
diff --git a/boot/Makefile b/boot/Makefile
index f828f87..10f0157 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -27,6 +27,7 @@
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EXTLINUX) += bootmeth_extlinux.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EXTLINUX_PXE) += bootmeth_pxe.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EFILOADER) += bootmeth_efi.o
+obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_CROS) += bootmeth_cros.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_SANDBOX) += bootmeth_sandbox.o
 obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_SCRIPT) += bootmeth_script.o
 ifdef CONFIG_$(SPL_TPL_)BOOTSTD_FULL
diff --git a/boot/bootflow.c b/boot/bootflow.c
index 8f2cb87..81b5829 100644
--- a/boot/bootflow.c
+++ b/boot/bootflow.c
@@ -12,7 +12,9 @@
 #include <bootmeth.h>
 #include <bootstd.h>
 #include <dm.h>
+#include <env_internal.h>
 #include <malloc.h>
+#include <serial.h>
 #include <dm/device-internal.h>
 #include <dm/uclass-internal.h>
 
@@ -315,14 +317,14 @@
 
 	/* If we got a valid bootflow, return it */
 	if (!ret) {
-		log_debug("Bootdevice '%s' part %d method '%s': Found bootflow\n",
+		log_debug("Bootdev '%s' part %d method '%s': Found bootflow\n",
 			  dev->name, iter->part, iter->method->name);
 		return 0;
 	}
 
 	/* Unless there is nothing more to try, move to the next device */
 	else if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) {
-		log_debug("Bootdevice '%s' part %d method '%s': Error %d\n",
+		log_debug("Bootdev '%s' part %d method '%s': Error %d\n",
 			  dev->name, iter->part, iter->method->name, ret);
 		/*
 		 * For 'all' we return all bootflows, even
@@ -552,3 +554,336 @@
 
 	return -ENOTSUPP;
 }
+
+/**
+ * bootflow_cmdline_set() - Set the command line for a bootflow
+ *
+ * @value: New command-line string
+ * Returns 0 if OK, -ENOENT if no current bootflow, -ENOMEM if out of memory
+ */
+int bootflow_cmdline_set(struct bootflow *bflow, const char *value)
+{
+	char *cmdline = NULL;
+
+	if (value) {
+		cmdline = strdup(value);
+		if (!cmdline)
+			return -ENOMEM;
+	}
+
+	free(bflow->cmdline);
+	bflow->cmdline = cmdline;
+
+	return 0;
+}
+
+#ifdef CONFIG_BOOTSTD_FULL
+/**
+ * on_bootargs() - Update the cmdline of a bootflow
+ */
+static int on_bootargs(const char *name, const char *value, enum env_op op,
+		       int flags)
+{
+	struct bootstd_priv *std;
+	struct bootflow *bflow;
+	int ret;
+
+	ret = bootstd_get_priv(&std);
+	if (ret)
+		return 0;
+	bflow = std->cur_bootflow;
+	if (!bflow)
+		return 0;
+
+	switch (op) {
+	case env_op_create:
+	case env_op_overwrite:
+		ret = bootflow_cmdline_set(bflow, value);
+		if (ret && ret != ENOENT)
+			return 1;
+		return 0;
+	case env_op_delete:
+		bootflow_cmdline_set(bflow, NULL);
+		fallthrough;
+	default:
+		return 0;
+	}
+}
+U_BOOT_ENV_CALLBACK(bootargs, on_bootargs);
+#endif
+
+/**
+ * copy_in() - Copy a string into a cmdline buffer
+ *
+ * @buf: Buffer to copy into
+ * @end: End of buffer (pointer to char after the end)
+ * @arg: String to copy from
+ * @len: Number of chars to copy from @arg (note that this is not usually the
+ * sane as strlen(arg) since the string may contain following arguments)
+ * @new_val: Value to put after arg, or BOOTFLOWCL_EMPTY to use an empty value
+ * with no '=' sign
+ * Returns: Number of chars written to @buf
+ */
+static int copy_in(char *buf, char *end, const char *arg, int len,
+		   const char *new_val)
+{
+	char *to = buf;
+
+	/* copy the arg name */
+	if (to + len >= end)
+		return -E2BIG;
+	memcpy(to, arg, len);
+	to += len;
+
+	if (new_val == BOOTFLOWCL_EMPTY) {
+		/* no value */
+	} else {
+		bool need_quote = strchr(new_val, ' ');
+		len = strlen(new_val);
+
+		/* need space for value, equals sign and maybe two quotes */
+		if (to + 1 + (need_quote ? 2 : 0) + len >= end)
+			return -E2BIG;
+		*to++ = '=';
+		if (need_quote)
+			*to++ = '"';
+		memcpy(to, new_val, len);
+		to += len;
+		if (need_quote)
+			*to++ = '"';
+	}
+
+	return to - buf;
+}
+
+int cmdline_set_arg(char *buf, int maxlen, const char *cmdline,
+		    const char *set_arg, const char *new_val, int *posp)
+{
+	bool found_arg = false;
+	const char *from;
+	char *to, *end;
+	int set_arg_len;
+	char empty = '\0';
+	int ret;
+
+	from = cmdline ?: &empty;
+
+	/* check if the value has quotes inside */
+	if (new_val && new_val != BOOTFLOWCL_EMPTY && strchr(new_val, '"'))
+		return -EBADF;
+
+	set_arg_len = strlen(set_arg);
+	for (to = buf, end = buf + maxlen; *from;) {
+		const char *val, *arg_end, *val_end, *p;
+		bool in_quote;
+
+		if (to >= end)
+			return -E2BIG;
+		while (*from == ' ')
+			from++;
+		if (!*from)
+			break;
+
+		/* find the end of this arg */
+		val = NULL;
+		arg_end = NULL;
+		val_end = NULL;
+		in_quote = false;
+		for (p = from;; p++) {
+			if (in_quote) {
+				if (!*p)
+					return -EINVAL;
+				if (*p == '"')
+					in_quote = false;
+				continue;
+			}
+			if (*p == '=') {
+				arg_end = p;
+				val = p + 1;
+			} else if (*p == '"') {
+				in_quote = true;
+			} else if (!*p || *p == ' ') {
+				val_end = p;
+				if (!arg_end)
+					arg_end = p;
+				break;
+			}
+		}
+		/*
+		 * At this point val_end points to the end of the value, or the
+		 * last char after the arg name, if there is no label.
+		 * arg_end is the char after the arg name
+		 * val points to the value, or NULL if there is none
+		 * char after the value.
+		 *
+		 *        fred=1234
+		 *        ^   ^^   ^
+		 *      from  ||   |
+		 *           / \    \
+		 *    arg_end  val   val_end
+		 */
+		log_debug("from %s arg_end %ld val %ld val_end %ld\n", from,
+			  (long)(arg_end - from), (long)(val - from),
+			  (long)(val_end - from));
+
+		if (to != buf) {
+			if (to >= end)
+				return -E2BIG;
+			*to++ = ' ';
+		}
+
+		/* if this is the target arg, update it */
+		if (!strncmp(from, set_arg, arg_end - from)) {
+			if (!buf) {
+				bool has_quote = val_end[-1] == '"';
+
+				/*
+				 * exclude any start/end quotes from
+				 * calculations
+				 */
+				if (!val)
+					val = val_end;
+				*posp = val - cmdline + has_quote;
+				return val_end - val - 2 * has_quote;
+			}
+			found_arg = true;
+			if (!new_val) {
+				/* delete this arg */
+				from = val_end + (*val_end == ' ');
+				log_debug("delete from: %s\n", from);
+				if (to != buf)
+					to--; /* drop the space we added */
+				continue;
+			}
+
+			ret = copy_in(to, end, from, arg_end - from, new_val);
+			if (ret < 0)
+				return ret;
+			to += ret;
+
+		/* if not the target arg, copy it unchanged */
+		} else if (to) {
+			int len;
+
+			len = val_end - from;
+			if (to + len >= end)
+				return -E2BIG;
+			memcpy(to, from, len);
+			to += len;
+		}
+		from = val_end;
+	}
+
+	/* If we didn't find the arg, add it */
+	if (!found_arg) {
+		/* trying to delete something that is not there */
+		if (!new_val || !buf)
+			return -ENOENT;
+		if (to >= end)
+			return -E2BIG;
+
+		/* add a space to separate it from the previous arg */
+		if (to != buf && to[-1] != ' ')
+			*to++ = ' ';
+		ret = copy_in(to, end, set_arg, set_arg_len, new_val);
+		log_debug("ret=%d, to: %s buf: %s\n", ret, to, buf);
+		if (ret < 0)
+			return ret;
+		to += ret;
+	}
+
+	/* delete any trailing space */
+	if (to > buf && to[-1] == ' ')
+		to--;
+
+	if (to >= end)
+		return -E2BIG;
+	*to++ = '\0';
+
+	return to - buf;
+}
+
+int bootflow_cmdline_set_arg(struct bootflow *bflow, const char *set_arg,
+			     const char *new_val, bool set_env)
+{
+	char buf[2048];
+	char *cmd = NULL;
+	int ret;
+
+	ret = cmdline_set_arg(buf, sizeof(buf), bflow->cmdline, set_arg,
+			      new_val, NULL);
+	if (ret < 0)
+		return ret;
+
+	ret = bootflow_cmdline_set(bflow, buf);
+	if (*buf) {
+		cmd = strdup(buf);
+		if (!cmd)
+			return -ENOMEM;
+	}
+	free(bflow->cmdline);
+	bflow->cmdline = cmd;
+
+	if (set_env) {
+		ret = env_set("bootargs", bflow->cmdline);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+int cmdline_get_arg(const char *cmdline, const char *arg, int *posp)
+{
+	int ret;
+
+	ret = cmdline_set_arg(NULL, 1, cmdline, arg, NULL, posp);
+
+	return ret;
+}
+
+int bootflow_cmdline_get_arg(struct bootflow *bflow, const char *arg,
+			     const char **val)
+{
+	int ret;
+	int pos;
+
+	ret = cmdline_get_arg(bflow->cmdline, arg, &pos);
+	if (ret < 0)
+		return ret;
+	*val = bflow->cmdline + pos;
+
+	return ret;
+}
+
+int bootflow_cmdline_auto(struct bootflow *bflow, const char *arg)
+{
+	struct serial_device_info info;
+	char buf[50];
+	int ret;
+
+	ret = serial_getinfo(gd->cur_serial_dev, &info);
+	if (ret)
+		return ret;
+
+	*buf = '\0';
+	if (!strcmp("earlycon", arg)) {
+		snprintf(buf, sizeof(buf),
+			 "uart8250,mmio32,%#lx,%dn8", info.addr,
+			 info.baudrate);
+	} else if (!strcmp("console", arg)) {
+		snprintf(buf, sizeof(buf),
+			 "ttyS0,%dn8", info.baudrate);
+	}
+
+	if (!*buf) {
+		printf("Unknown param '%s\n", arg);
+		return -ENOENT;
+	}
+
+	ret = bootflow_cmdline_set_arg(bflow, arg, buf, true);
+	if (ret)
+		return ret;
+
+	return 0;
+}
diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c
index 701ee8a..eeded08 100644
--- a/boot/bootmeth-uclass.c
+++ b/boot/bootmeth-uclass.c
@@ -395,7 +395,7 @@
 /**
  * on_bootmeths() - Update the bootmeth order
  *
- * This will check for a valid baudrate and only apply it if valid.
+ * This will check for a valid list of bootmeths and only apply it if valid.
  */
 static int on_bootmeths(const char *name, const char *value, enum env_op op,
 			int flags)
diff --git a/boot/bootmeth_cros.c b/boot/bootmeth_cros.c
new file mode 100644
index 0000000..aa19ae0
--- /dev/null
+++ b/boot/bootmeth_cros.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bootmethod for ChromiumOS
+ *
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <common.h>
+#include <blk.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootmeth.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>
+
+enum {
+	/* Offsets in the kernel-partition header */
+	KERN_START	= 0x4f0,
+	KERN_SIZE	= 0x518,
+
+	SETUP_OFFSET	= 0x1000,	/* bytes before base */
+	CMDLINE_OFFSET	= 0x2000,	/* bytes before base */
+	OFFSET_BASE	= 0x100000,	/* assumed kernel load-address */
+};
+
+static int cros_check(struct udevice *dev, struct bootflow_iter *iter)
+{
+	/* This only works on block and network devices */
+	if (bootflow_iter_check_blk(iter))
+		return log_msg_ret("blk", -ENOTSUPP);
+
+	return 0;
+}
+
+static int copy_cmdline(const char *from, const char *uuid, char **bufp)
+{
+	const int maxlen = 2048;
+	char buf[maxlen];
+	char *cmd, *to, *end;
+	int len;
+
+	/* Allow space for cmdline + UUID */
+	len = strnlen(from, sizeof(buf));
+	if (len >= maxlen)
+		return -E2BIG;
+
+	log_debug("uuid %d %s\n", uuid ? (int)strlen(uuid) : 0, uuid);
+	for (to = buf, end = buf + maxlen - UUID_STR_LEN - 1; *from; from++) {
+		if (to >= end)
+			return -E2BIG;
+		if (from[0] == '%' && from[1] == 'U' && uuid &&
+		    strlen(uuid) == UUID_STR_LEN) {
+			strcpy(to, uuid);
+			to += UUID_STR_LEN;
+			from++;
+		} else {
+			*to++ = *from;
+		}
+	}
+	*to = '\0';
+	len = to - buf;
+	cmd = strdup(buf);
+	if (!cmd)
+		return -ENOMEM;
+	free(*bufp);
+	*bufp = cmd;
+
+	return 0;
+}
+
+static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+{
+	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;
+	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);
+	if (ret)
+		return log_msg_ret("part", ret);
+
+	/* Make a buffer for the header information */
+	num_blks = SZ_4K >> 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);
+	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);
+
+	if (memcmp("CHROMEOS", hdr, 8))
+		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;
+
+	buf = memalign(SZ_1K, size);
+	if (!buf)
+		return log_msg_ret("buf", -ENOMEM);
+	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);
+	if (ret != num_blks)
+		return log_msg_ret("inf", ret);
+	base = map_to_sysmem(buf);
+
+	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, cmdline, kern_base);
+
+#ifdef CONFIG_X86
+	const char *version;
+
+	version = zimage_get_kernel_version(map_sysmem(setup, 0),
+					    map_sysmem(kern_base, 0));
+	log_debug("version %s\n", version);
+	if (version)
+		bflow->name = strdup(version);
+#endif
+	if (!bflow->name)
+		bflow->name = strdup("ChromeOS");
+	if (!bflow->name)
+		return log_msg_ret("nam", -ENOMEM);
+	bflow->os_name = strdup("ChromeOS");
+	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->state = BOOTFLOWST_READY;
+	bflow->buf = buf;
+	bflow->x86_setup = map_sysmem(setup, 0);
+
+	return 0;
+}
+
+static int cros_read_file(struct udevice *dev, struct bootflow *bflow,
+			 const char *file_path, ulong addr, ulong *sizep)
+{
+	return -ENOSYS;
+}
+
+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
+
+	return log_msg_ret("go", -EFAULT);
+}
+
+static int cros_bootmeth_bind(struct udevice *dev)
+{
+	struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
+
+	plat->desc = "ChromiumOS boot";
+
+	return 0;
+}
+
+static struct bootmeth_ops cros_bootmeth_ops = {
+	.check		= cros_check,
+	.read_bootflow	= cros_read_bootflow,
+	.read_file	= cros_read_file,
+	.boot		= cros_boot,
+};
+
+static const struct udevice_id cros_bootmeth_ids[] = {
+	{ .compatible = "u-boot,cros" },
+	{ }
+};
+
+U_BOOT_DRIVER(bootmeth_cros) = {
+	.name		= "bootmeth_cros",
+	.id		= UCLASS_BOOTMETH,
+	.of_match	= cros_bootmeth_ids,
+	.ops		= &cros_bootmeth_ops,
+	.bind		= cros_bootmeth_bind,
+};
diff --git a/boot/bootmeth_qfw.c b/boot/bootmeth_qfw.c
index ecd4b08..8ebbc3e 100644
--- a/boot/bootmeth_qfw.c
+++ b/boot/bootmeth_qfw.c
@@ -76,7 +76,7 @@
 {
 	struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
 
-	plat->desc = "Sandbox boot for testing";
+	plat->desc = "QEMU boot using firmware interface";
 
 	return 0;
 }
diff --git a/cmd/Kconfig b/cmd/Kconfig
index fd76972..ecfd575 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -135,6 +135,14 @@
 	help
 	  Print board info
 
+config CMD_BDINFO_EXTRA
+	bool "bdinfo extra features"
+	default y if SANDBOX || X86
+	help
+	  Show additional information about the board. This uses a little more
+	  code space but provides more options, particularly those useful for
+	  bringup, development and debugging.
+
 config CMD_CONFIG
 	bool "config"
 	default SANDBOX
diff --git a/cmd/acpi.c b/cmd/acpi.c
index e70913e..ede9c8c 100644
--- a/cmd/acpi.c
+++ b/cmd/acpi.c
@@ -118,6 +118,22 @@
 	return 0;
 }
 
+static int do_acpi_set(struct cmd_tbl *cmdtp, int flag, int argc,
+		       char *const argv[])
+{
+	ulong val;
+
+	if (argc < 2) {
+		printf("ACPI pointer: %lx\n", gd_acpi_start());
+	} else {
+		val = hextoul(argv[1], NULL);
+		printf("Setting ACPI pointer to %lx\n", val);
+		gd_set_acpi_start(val);
+	}
+
+	return 0;
+}
+
 static int do_acpi_items(struct cmd_tbl *cmdtp, int flag, int argc,
 			 char *const argv[])
 {
@@ -157,12 +173,14 @@
 
 #ifdef CONFIG_SYS_LONGHELP
 static char acpi_help_text[] =
-	"list - list ACPI tables\n"
-	"acpi items [-d]  - List/dump each piece of ACPI data from devices\n"
-	"acpi dump <name> - Dump ACPI table";
+	"list  - list ACPI tables\n"
+	"acpi items [-d]   - List/dump each piece of ACPI data from devices\n"
+	"acpi set [<addr>] - Set or show address of ACPI tables\n"
+	"acpi dump <name>  - Dump ACPI table";
 #endif
 
 U_BOOT_CMD_WITH_SUBCMDS(acpi, "ACPI tables", acpi_help_text,
 	U_BOOT_SUBCMD_MKENT(list, 1, 1, do_acpi_list),
 	U_BOOT_SUBCMD_MKENT(items, 2, 1, do_acpi_items),
+	U_BOOT_SUBCMD_MKENT(set, 2, 1, do_acpi_set),
 	U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_acpi_dump));
diff --git a/cmd/bdinfo.c b/cmd/bdinfo.c
index 365357c..1fe13ca 100644
--- a/cmd/bdinfo.c
+++ b/cmd/bdinfo.c
@@ -13,6 +13,7 @@
 #include <lmb.h>
 #include <mapmem.h>
 #include <net.h>
+#include <serial.h>
 #include <video.h>
 #include <vsprintf.h>
 #include <asm/cache.h>
@@ -113,6 +114,25 @@
 	}
 }
 
+static void print_serial(struct udevice *dev)
+{
+	struct serial_device_info info;
+	int ret;
+
+	if (!dev || !IS_ENABLED(CONFIG_DM_SERIAL))
+		return;
+
+	ret = serial_getinfo(dev, &info);
+	if (ret)
+		return;
+
+	bdinfo_print_num_l("serial addr", info.addr);
+	bdinfo_print_num_l(" width", info.reg_width);
+	bdinfo_print_num_l(" shift", info.reg_shift);
+	bdinfo_print_num_l(" offset", info.reg_offset);
+	bdinfo_print_num_l(" clock", info.clock);
+}
+
 int do_bdinfo(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 {
 	struct bd_info *bd = gd->bd;
@@ -151,6 +171,13 @@
 		if (IS_ENABLED(CONFIG_OF_REAL))
 			printf("devicetree  = %s\n", fdtdec_get_srcname());
 	}
+	print_serial(gd->cur_serial_dev);
+
+	if (IS_ENABLED(CONFIG_CMD_BDINFO_EXTRA)) {
+		bdinfo_print_num_ll("stack ptr", (ulong)&bd);
+		bdinfo_print_num_ll("ram_top ptr", (ulong)gd->ram_top);
+		bdinfo_print_num_l("malloc base", gd_malloc_start());
+	}
 
 	arch_print_bdinfo();
 
diff --git a/cmd/bootflow.c b/cmd/bootflow.c
index 5c61286..c0aa4f8 100644
--- a/cmd/bootflow.c
+++ b/cmd/bootflow.c
@@ -288,6 +288,12 @@
 		return CMD_RET_FAILURE;
 	}
 	std->cur_bootflow = found;
+	if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) {
+		if (env_set("bootargs", found->cmdline)) {
+			printf("Cannot set bootargs\n");
+			return CMD_RET_FAILURE;
+		}
+	}
 
 	return 0;
 }
@@ -324,6 +330,14 @@
 	printf("Buffer:    %lx\n", (ulong)map_to_sysmem(bflow->buf));
 	printf("Size:      %x (%d bytes)\n", bflow->size, bflow->size);
 	printf("OS:        %s\n", bflow->os_name ? bflow->os_name : "(none)");
+	printf("Cmdline:   ");
+	if (bflow->cmdline)
+		puts(bflow->cmdline);
+	else
+		puts("(none)");
+	putc('\n');
+	if (bflow->x86_setup)
+		printf("X86 setup: %p\n", bflow->x86_setup);
 	printf("Logo:      %s\n", bflow->logo ?
 	       simple_xtoa((ulong)map_to_sysmem(bflow->logo)) : "(none)");
 	if (bflow->logo) {
@@ -417,6 +431,75 @@
 
 	return 0;
 }
+
+static int do_bootflow_cmdline(struct cmd_tbl *cmdtp, int flag, int argc,
+			       char *const argv[])
+{
+	struct bootstd_priv *std;
+	struct bootflow *bflow;
+	const char *op, *arg, *val = NULL;
+	int ret;
+
+	if (argc < 3)
+		return CMD_RET_USAGE;
+
+	ret = bootstd_get_priv(&std);
+	if (ret)
+		return CMD_RET_FAILURE;
+
+	bflow = std->cur_bootflow;
+	if (!bflow) {
+		printf("No bootflow selected\n");
+		return CMD_RET_FAILURE;
+	}
+
+	op = argv[1];
+	arg = argv[2];
+	if (*op == 's') {
+		if (argc < 4)
+			return CMD_RET_USAGE;
+		val = argv[3];
+	}
+
+	switch (*op) {
+	case 'c':	/* clear */
+		val = "";
+		fallthrough;
+	case 's':	/* set */
+	case 'd':	/* delete */
+		ret = bootflow_cmdline_set_arg(bflow, arg, val, true);
+		break;
+	case 'g':	/* get */
+		ret = bootflow_cmdline_get_arg(bflow, arg, &val);
+		if (ret >= 0)
+			printf("%.*s\n", ret, val);
+		break;
+	case 'a':	/* auto */
+		ret = bootflow_cmdline_auto(bflow, arg);
+		break;
+	}
+	switch (ret) {
+	case -E2BIG:
+		printf("Argument too long\n");
+		break;
+	case -ENOENT:
+		printf("Argument not found\n");
+		break;
+	case -EINVAL:
+		printf("Mismatched quotes\n");
+		break;
+	case -EBADF:
+		printf("Value must be quoted\n");
+		break;
+	default:
+		if (ret < 0)
+			printf("Unknown error: %dE\n", ret);
+	}
+	if (ret < 0)
+		return CMD_RET_FAILURE;
+
+	return 0;
+}
 #endif /* CONFIG_CMD_BOOTFLOW_FULL */
 
 #ifdef CONFIG_SYS_LONGHELP
@@ -427,7 +510,8 @@
 	"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 menu [-t]             - show a menu of available bootflows";
+	"bootflow menu [-t]             - show a menu of available bootflows\n"
+	"bootflow cmdline [set|get|clear|delete|auto] <param> [<value>] - update cmdline";
 #else
 	"scan - boot first available bootflow\n";
 #endif
@@ -441,5 +525,6 @@
 	U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info),
 	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),
 #endif
 );
diff --git a/cmd/part.c b/cmd/part.c
index 28f2b7f..0ce1900 100644
--- a/cmd/part.c
+++ b/cmd/part.c
@@ -182,6 +182,36 @@
 	return do_part_info(argc, argv, CMD_PART_INFO_NUMBER);
 }
 
+static int do_part_set(int argc, char *const argv[])
+{
+	const char *devname, *partstr, *typestr;
+	struct blk_desc *desc;
+	int dev;
+
+	if (argc < 3)
+		return CMD_RET_USAGE;
+
+	/* Look up the device */
+	devname = argv[0];
+	partstr = argv[1];
+	typestr = argv[2];
+	dev = blk_get_device_by_str(devname, partstr, &desc);
+	if (dev < 0) {
+		printf("** Bad device specification %s %s **\n", devname,
+		       partstr);
+		return CMD_RET_FAILURE;
+	}
+
+	desc->part_type = part_get_type_by_name(typestr);
+	if (!desc->part_type) {
+		printf("Unknown partition type '%s'\n", typestr);
+		return CMD_RET_FAILURE;
+	}
+	part_print(desc);
+
+	return 0;
+}
+
 #ifdef CONFIG_PARTITION_TYPE_GUID
 static int do_part_type(int argc, char *const argv[])
 {
@@ -245,6 +275,8 @@
 		return do_part_number(argc - 2, argv + 2);
 	else if (!strcmp(argv[1], "types"))
 		return do_part_types(argc - 2, argv + 2);
+	else if (!strcmp(argv[1], "set"))
+		return do_part_set(argc - 2, argv + 2);
 #ifdef CONFIG_PARTITION_TYPE_GUID
 	else if (!strcmp(argv[1], "type"))
 		return do_part_type(argc - 2, argv + 2);
@@ -279,6 +311,8 @@
 #endif
 	"part type <interface> <dev>:<part> <varname>\n"
 	"    - set environment variable to partition type\n"
+	"part set <interface> <dev> type\n"
+	"    - set partition type for a device\n"
 	"part types\n"
 	"    - list supported partition table types"
 );
diff --git a/cmd/qfw.c b/cmd/qfw.c
index ae3c6a7..d6ecfa6 100644
--- a/cmd/qfw.c
+++ b/cmd/qfw.c
@@ -26,7 +26,7 @@
 	for (file = qfw_file_iter_init(qfw_dev, &iter);
 	     !qfw_file_iter_end(&iter);
 	     file = qfw_file_iter_next(&iter)) {
-		printf("%-56s\n", file->cfg.name);
+		printf("%08lx %-56s\n", file->addr, file->cfg.name);
 	}
 
 	return 0;
diff --git a/cmd/x86/mtrr.c b/cmd/x86/mtrr.c
index b1691d8..6ad7a12 100644
--- a/cmd/x86/mtrr.c
+++ b/cmd/x86/mtrr.c
@@ -10,71 +10,19 @@
 #include <asm/mp.h>
 #include <asm/mtrr.h>
 
-static const char *const mtrr_type_name[MTRR_TYPE_COUNT] = {
-	"Uncacheable",
-	"Combine",
-	"2",
-	"3",
-	"Through",
-	"Protect",
-	"Back",
-};
-
-static void read_mtrrs(void *arg)
-{
-	struct mtrr_info *info = arg;
-
-	mtrr_read_all(info);
-}
-
-static int do_mtrr_list(int reg_count, int cpu_select)
-{
-	struct mtrr_info info;
-	int ret;
-	int i;
-
-	printf("Reg Valid Write-type   %-16s %-16s %-16s\n", "Base   ||",
-	       "Mask   ||", "Size   ||");
-	memset(&info, '\0', sizeof(info));
-	ret = mp_run_on_cpus(cpu_select, read_mtrrs, &info);
-	if (ret)
-		return log_msg_ret("run", ret);
-	for (i = 0; i < reg_count; i++) {
-		const char *type = "Invalid";
-		uint64_t base, mask, size;
-		bool valid;
-
-		base = info.mtrr[i].base;
-		mask = info.mtrr[i].mask;
-		size = ~mask & ((1ULL << CONFIG_CPU_ADDR_BITS) - 1);
-		size |= (1 << 12) - 1;
-		size += 1;
-		valid = mask & MTRR_PHYS_MASK_VALID;
-		type = mtrr_type_name[base & MTRR_BASE_TYPE_MASK];
-		printf("%d   %-5s %-12s %016llx %016llx %016llx\n", i,
-		       valid ? "Y" : "N", type, base & ~MTRR_BASE_TYPE_MASK,
-		       mask & ~MTRR_PHYS_MASK_VALID, size);
-	}
-
-	return 0;
-}
-
 static int do_mtrr_set(int cpu_select, uint reg, int argc, char *const argv[])
 {
 	const char *typename = argv[0];
 	uint32_t start, size;
 	uint64_t base, mask;
-	int i, type = -1;
+	int type = -1;
 	bool valid;
 	int ret;
 
 	if (argc < 3)
 		return CMD_RET_USAGE;
-	for (i = 0; i < MTRR_TYPE_COUNT; i++) {
-		if (*typename == *mtrr_type_name[i])
-			type = i;
-	}
-	if (type == -1) {
+	type = mtrr_get_type_by_name(typename);
+	if (type < 0) {
 		printf("Invalid type name %s\n", typename);
 		return CMD_RET_USAGE;
 	}
@@ -146,7 +94,7 @@
 			if (!first)
 				printf("\n");
 			printf("CPU %d:\n", i);
-			ret = do_mtrr_list(reg_count, i);
+			ret = mtrr_list(reg_count, i);
 			if (ret) {
 				printf("Failed to read CPU %s (err=%d)\n",
 				       i < MP_SELECT_ALL ? simple_itoa(i) : "",
diff --git a/common/bloblist.c b/common/bloblist.c
index 0d63b6e..2144b10 100644
--- a/common/bloblist.c
+++ b/common/bloblist.c
@@ -51,6 +51,7 @@
 
 	/* BLOBLISTT_PROJECT_AREA */
 	{ BLOBLISTT_U_BOOT_SPL_HANDOFF, "SPL hand-off" },
+	{ BLOBLISTT_U_BOOT_VIDEO, "SPL video handoff" },
 
 	/* BLOBLISTT_VENDOR_AREA */
 };
diff --git a/common/board_f.c b/common/board_f.c
index 334d04a..e5969ec 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -729,8 +729,7 @@
 #endif
 
 /* ARM calls relocate_code from its crt0.S */
-#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
-		!CONFIG_IS_ENABLED(X86_64)
+#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX)
 
 static int jump_to_copy(void)
 {
@@ -752,7 +751,11 @@
 	 * (CPU cache)
 	 */
 	arch_setup_gd(gd->new_gd);
-	board_init_f_r_trampoline(gd->start_addr_sp);
+# if CONFIG_IS_ENABLED(X86_64)
+		board_init_f_r_trampoline64(gd->new_gd, gd->start_addr_sp);
+# else
+		board_init_f_r_trampoline(gd->start_addr_sp);
+# endif
 #else
 	relocate_code(gd->start_addr_sp, gd->new_gd, gd->relocaddr);
 #endif
@@ -967,8 +970,7 @@
 	 * watchdog device is not serviced is as small as possible.
 	 */
 	cyclic_unregister_all,
-#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
-		!CONFIG_IS_ENABLED(X86_64)
+#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX)
 	jump_to_copy,
 #endif
 	NULL,
diff --git a/common/board_r.c b/common/board_r.c
index d798c00..4aaa894 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -196,7 +196,7 @@
 
 static int initr_malloc(void)
 {
-	ulong malloc_start;
+	ulong start;
 
 #if CONFIG_VAL(SYS_MALLOC_F_LEN)
 	debug("Pre-reloc malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr,
@@ -207,8 +207,9 @@
 	 * This value MUST match the value of gd->start_addr_sp in board_f.c:
 	 * reserve_noncached().
 	 */
-	malloc_start = gd->relocaddr - TOTAL_MALLOC_LEN;
-	mem_malloc_init((ulong)map_sysmem(malloc_start, TOTAL_MALLOC_LEN),
+	start = gd->relocaddr - TOTAL_MALLOC_LEN;
+	gd_set_malloc_start(start);
+	mem_malloc_init((ulong)map_sysmem(start, TOTAL_MALLOC_LEN),
 			TOTAL_MALLOC_LEN);
 	return 0;
 }
diff --git a/common/log.c b/common/log.c
index 6f02a25..b2de57f 100644
--- a/common/log.c
+++ b/common/log.c
@@ -437,7 +437,7 @@
 	/*
 	 * We cannot add runtime data to the driver since it is likely stored
 	 * in rodata. Instead, set up a 'device' corresponding to each driver.
-	 * We only support having a single device.
+	 * We only support having a single device for each driver.
 	 */
 	INIT_LIST_HEAD((struct list_head *)&gd->log_head);
 	while (drv < end) {
diff --git a/common/log_console.c b/common/log_console.c
index f1dcc04..bb091ce 100644
--- a/common/log_console.c
+++ b/common/log_console.c
@@ -37,8 +37,14 @@
 			printf("%s:", rec->file);
 		if (fmt & BIT(LOGF_LINE))
 			printf("%d-", rec->line);
-		if (fmt & BIT(LOGF_FUNC))
-			printf("%*s()", CONFIG_LOGF_FUNC_PAD, rec->func);
+		if (fmt & BIT(LOGF_FUNC)) {
+			if (CONFIG_IS_ENABLED(USE_TINY_PRINTF)) {
+				printf("%s()", rec->func);
+			} else {
+				printf("%*s()", CONFIG_LOGF_FUNC_PAD,
+				       rec->func);
+			}
+		}
 	}
 	if (fmt & BIT(LOGF_MSG))
 		printf("%s%s", add_space ? " " : "", rec->msg);
diff --git a/configs/chromebook_coral_defconfig b/configs/chromebook_coral_defconfig
index f5995f2..fe61153 100644
--- a/configs/chromebook_coral_defconfig
+++ b/configs/chromebook_coral_defconfig
@@ -1,5 +1,6 @@
 CONFIG_X86=y
 CONFIG_TEXT_BASE=0x1110000
+CONFIG_SYS_MALLOC_LEN=0x2000000
 CONFIG_SYS_MALLOC_F_LEN=0x3d00
 CONFIG_NR_DRAM_BANKS=8
 CONFIG_MAX_CPUS=8
@@ -22,6 +23,7 @@
 CONFIG_X86_OFFSET_SPL=0xffe80000
 CONFIG_INTEL_ACPIGEN=y
 CONFIG_INTEL_GENERIC_WIFI=y
+CONFIG_BOOTSTD_FULL=y
 CONFIG_SYS_MONITOR_BASE=0x01110000
 CONFIG_CHROMEOS=y
 CONFIG_BOOTSTAGE=y
@@ -33,8 +35,10 @@
 CONFIG_USE_BOOTARGS=y
 CONFIG_BOOTARGS_SUBST=y
 CONFIG_USE_BOOTCOMMAND=y
-CONFIG_BOOTCOMMAND="tpm init; tpm startup TPM2_SU_CLEAR; read mmc 0:2 100000 0 80; setexpr loader *001004f0; setexpr size *00100518; setexpr blocks $size / 200; read mmc 0:2 100000 80 $blocks; setexpr setup $loader - 1000; setexpr cmdline_ptr $loader - 2000; setexpr.s cmdline *$cmdline_ptr; setexpr cmdline gsub %U \\\\${uuid}; if part uuid mmc 0:2 uuid; then zboot start 100000 0 0 0 $setup cmdline; zboot load; zboot setup; zboot dump; zboot go;fi"
+CONFIG_BOOTCOMMAND="tpm init; tpm startup TPM2_SU_CLEAR; bootflow scan -lb"
 CONFIG_SYS_CONSOLE_INFO_QUIET=y
+CONFIG_LOG=y
+CONFIG_LOGF_FUNC=y
 CONFIG_DISPLAY_BOARDINFO_LATE=y
 CONFIG_LAST_STAGE_INIT=y
 CONFIG_BLOBLIST=y
@@ -52,9 +56,11 @@
 CONFIG_TPL_SYS_MALLOC_SIMPLE=y
 CONFIG_TPL_POWER=y
 CONFIG_HUSH_PARSER=y
-CONFIG_SYS_PBSIZE=532
+CONFIG_SYS_CBSIZE=1024
+CONFIG_SYS_PBSIZE=1024
 CONFIG_CMD_CPU=y
 CONFIG_CMD_PMC=y
+CONFIG_CMD_MEM_SEARCH=y
 CONFIG_CMD_GPIO=y
 CONFIG_CMD_I2C=y
 CONFIG_CMD_PART=y
diff --git a/configs/coreboot_defconfig b/configs/coreboot_defconfig
index 0f3512d..fc935610 100644
--- a/configs/coreboot_defconfig
+++ b/configs/coreboot_defconfig
@@ -1,5 +1,6 @@
 CONFIG_X86=y
 CONFIG_TEXT_BASE=0x1110000
+CONFIG_SYS_MALLOC_LEN=0x2000000
 CONFIG_NR_DRAM_BANKS=8
 CONFIG_ENV_SIZE=0x1000
 CONFIG_DEFAULT_DEVICE_TREE="coreboot"
@@ -8,12 +9,11 @@
 CONFIG_TARGET_COREBOOT=y
 CONFIG_FIT=y
 CONFIG_FIT_SIGNATURE=y
+CONFIG_BOOTSTD_FULL=y
 CONFIG_SYS_MONITOR_BASE=0x01110000
 CONFIG_SHOW_BOOT_PROGRESS=y
 CONFIG_USE_BOOTARGS=y
 CONFIG_BOOTARGS="root=/dev/sdb3 init=/sbin/init rootwait ro"
-CONFIG_USE_BOOTCOMMAND=y
-CONFIG_BOOTCOMMAND="ext2load scsi 0:3 01000000 /boot/vmlinuz; zboot 01000000"
 CONFIG_PRE_CONSOLE_BUFFER=y
 CONFIG_SYS_CONSOLE_INFO_QUIET=y
 CONFIG_LOG=y
@@ -23,9 +23,6 @@
 CONFIG_LAST_STAGE_INIT=y
 CONFIG_PCI_INIT_R=y
 CONFIG_HUSH_PARSER=y
-CONFIG_SYS_PBSIZE=532
-CONFIG_CMD_MEM_SEARCH=y
-CONFIG_CMD_IDE=y
 CONFIG_CMD_MMC=y
 CONFIG_CMD_PART=y
 CONFIG_CMD_USB=y
@@ -52,13 +49,6 @@
 CONFIG_REGMAP=y
 CONFIG_SYSCON=y
 # CONFIG_ACPIGEN is not set
-CONFIG_SYS_IDE_MAXDEVICE=4
-CONFIG_SYS_ATA_DATA_OFFSET=0
-CONFIG_SYS_ATA_REG_OFFSET=0
-CONFIG_SYS_ATA_ALT_OFFSET=0
-CONFIG_ATAPI=y
-CONFIG_LBA48=y
-CONFIG_SYS_64BIT_LBA=y
 CONFIG_NVME_PCI=y
 # CONFIG_PCI_PNP is not set
 CONFIG_SOUND=y
diff --git a/configs/qemu-x86_64_defconfig b/configs/qemu-x86_64_defconfig
index 6a539a8..4a15c51 100644
--- a/configs/qemu-x86_64_defconfig
+++ b/configs/qemu-x86_64_defconfig
@@ -6,30 +6,39 @@
 CONFIG_MAX_CPUS=2
 CONFIG_SPL_DM_SPI=y
 CONFIG_DEFAULT_DEVICE_TREE="qemu-x86_i440fx"
-CONFIG_SPL_TEXT_BASE=0xfffd0000
+CONFIG_SPL_TEXT_BASE=0xfffd8000
+CONFIG_SPL_SYS_MALLOC_F_LEN=0x2000
 CONFIG_DEBUG_UART_BASE=0x3f8
 CONFIG_DEBUG_UART_CLOCK=1843200
 CONFIG_X86_RUN_64BIT=y
 CONFIG_TARGET_QEMU_X86_64=y
 CONFIG_DEBUG_UART=y
+# CONFIG_HAVE_MICROCODE is not set
 CONFIG_SMP=y
 CONFIG_GENERATE_PIRQ_TABLE=y
 CONFIG_GENERATE_MP_TABLE=y
 CONFIG_X86_OFFSET_U_BOOT=0xfff00000
 CONFIG_FIT=y
 CONFIG_SPL_LOAD_FIT=y
+CONFIG_BOOTSTD_FULL=y
 CONFIG_SYS_MONITOR_BASE=0x01110000
-CONFIG_DISTRO_DEFAULTS=y
+CONFIG_BOOTSTD_DEFAULTS=y
 CONFIG_BOOTSTAGE=y
 CONFIG_BOOTSTAGE_REPORT=y
 CONFIG_SHOW_BOOT_PROGRESS=y
 CONFIG_USE_BOOTARGS=y
 CONFIG_BOOTARGS="root=/dev/sdb3 init=/sbin/init rootwait ro"
 CONFIG_SYS_CONSOLE_INFO_QUIET=y
+CONFIG_LOG=y
+CONFIG_LOGF_FUNC=y
+CONFIG_SPL_LOG=y
 CONFIG_DISPLAY_BOARDINFO_LATE=y
 CONFIG_LAST_STAGE_INIT=y
 CONFIG_PCI_INIT_R=y
+CONFIG_BLOBLIST=y
+CONFIG_BLOBLIST_ADDR=0x10000
 CONFIG_SPL_NO_BSS_LIMIT=y
+CONFIG_SPL_BOARD_INIT=y
 CONFIG_SPL_SYS_MALLOC_SIMPLE=y
 CONFIG_SPL_CPU=y
 CONFIG_SPL_ENV_SUPPORT=y
@@ -42,11 +51,13 @@
 CONFIG_CMD_CPU=y
 CONFIG_CMD_BOOTEFI_SELFTEST=y
 CONFIG_CMD_NVEDIT_EFI=y
+CONFIG_CMD_MEM_SEARCH=y
 CONFIG_CMD_IDE=y
 CONFIG_CMD_SPI=y
 CONFIG_CMD_USB=y
 # CONFIG_CMD_SETEXPR is not set
 CONFIG_BOOTP_BOOTFILESIZE=y
+CONFIG_CMD_EFIDEBUG=y
 CONFIG_CMD_TIME=y
 CONFIG_CMD_QFW=y
 CONFIG_CMD_BOOTSTAGE=y
@@ -68,13 +79,13 @@
 CONFIG_SYS_64BIT_LBA=y
 CONFIG_CPU=y
 CONFIG_NVME_PCI=y
+CONFIG_SPL_PCI_PNP=y
 CONFIG_SPL_DM_RTC=y
 CONFIG_SYS_NS16550_PORT_MAPPED=y
 CONFIG_SPI=y
 CONFIG_USB_KEYBOARD=y
-CONFIG_FRAMEBUFFER_SET_VESA_MODE=y
-CONFIG_FRAMEBUFFER_VESA_MODE_USER=y
-CONFIG_FRAMEBUFFER_VESA_MODE=0x144
 CONFIG_CONSOLE_SCROLL_LINES=5
+CONFIG_FAT_BLK_XLATE=y
+# CONFIG_SPL_USE_TINY_PRINTF is not set
 CONFIG_GENERATE_ACPI_TABLE=y
 # CONFIG_GZIP is not set
diff --git a/configs/qemu-x86_defconfig b/configs/qemu-x86_defconfig
index a7c37b8..95a6ff9 100644
--- a/configs/qemu-x86_defconfig
+++ b/configs/qemu-x86_defconfig
@@ -9,7 +9,7 @@
 CONFIG_GENERATE_PIRQ_TABLE=y
 CONFIG_GENERATE_MP_TABLE=y
 CONFIG_FIT=y
-CONFIG_DISTRO_DEFAULTS=y
+CONFIG_BOOTSTD_DEFAULTS=y
 CONFIG_BOOTSTAGE=y
 CONFIG_BOOTSTAGE_REPORT=y
 CONFIG_SHOW_BOOT_PROGRESS=y
@@ -53,9 +53,6 @@
 CONFIG_SYS_NS16550_PORT_MAPPED=y
 CONFIG_SPI=y
 CONFIG_USB_KEYBOARD=y
-CONFIG_FRAMEBUFFER_SET_VESA_MODE=y
-CONFIG_FRAMEBUFFER_VESA_MODE_USER=y
-CONFIG_FRAMEBUFFER_VESA_MODE=0x144
 CONFIG_CONSOLE_SCROLL_LINES=5
 CONFIG_GENERATE_ACPI_TABLE=y
 # CONFIG_GZIP is not set
diff --git a/configs/tools-only_defconfig b/configs/tools-only_defconfig
index 2bc3bd9..3f588ea 100644
--- a/configs/tools-only_defconfig
+++ b/configs/tools-only_defconfig
@@ -10,6 +10,7 @@
 CONFIG_TIMESTAMP=y
 CONFIG_FIT_SIGNATURE=y
 # CONFIG_BOOTSTD_FULL is not set
+# CONFIG_BOOTMETH_CROS is not set
 # CONFIG_BOOTMETH_VBE is not set
 CONFIG_USE_BOOTCOMMAND=y
 CONFIG_BOOTCOMMAND="run distro_bootcmd"
diff --git a/disk/part.c b/disk/part.c
index 35300df5..1d2117a 100644
--- a/disk/part.c
+++ b/disk/part.c
@@ -54,6 +54,22 @@
 	return NULL;
 }
 
+int part_get_type_by_name(const char *name)
+{
+	struct part_driver *drv =
+		ll_entry_start(struct part_driver, part_driver);
+	const int n_ents = ll_entry_count(struct part_driver, part_driver);
+	struct part_driver *entry;
+
+	for (entry = drv; entry != drv + n_ents; entry++) {
+		if (!strcasecmp(name, entry->name))
+			return entry->part_type;
+	}
+
+	/* Not found */
+	return PART_TYPE_UNKNOWN;
+}
+
 static struct blk_desc *get_dev_hwpart(const char *ifname, int dev, int hwpart)
 {
 	struct blk_desc *dev_desc;
diff --git a/doc/usage/cmd/acpi.rst b/doc/usage/cmd/acpi.rst
index 14bafc8..6b9b894 100644
--- a/doc/usage/cmd/acpi.rst
+++ b/doc/usage/cmd/acpi.rst
@@ -11,12 +11,14 @@
     acpi list
     acpi items [-d]
     acpi dump <name>
+    acpi set <address>
 
 Description
 -----------
 
-The *acpi* command is used to dump the ACPI tables generated by U-Boot for passing
-to the operating systems.
+The *acpi* command is used to dump the ACPI tables generated by U-Boot for
+passing to the operating systems. It allows manually setting the address to take
+a look at existing ACPI tables.
 
 ACPI tables can be generated by various output functions and even devices can
 output material to include in the Differentiated System Description Table (DSDT)
@@ -231,5 +233,28 @@
     00000000: 44 53 44 54 ea 32 00 00 02 eb 55 2d 42 4f 4f 54  DSDT.2....U-BOOT
     00000010: 55 2d 42 4f 4f 54 42 4c 25 07 11 20 49 4e 54 4c  U-BOOTBL%.. INTL
 
+This shows searching for tables in a known area of memory, then setting the
+pointer::
+
+    => acpi list
+    No ACPI tables present
+    => ms.s bff00000 80000 "RSD PTR"
+    bff75000: 52 53 44 20 50 54 52 20 cf 42 4f 43 48 53 20 00  RSD PTR .BOCHS .
+    1 match
+    => acpi set bff75000
+    Setting ACPI pointer to bff75000
+    => acpi list
+    Name      Base   Size  Detail
+    ----  --------  -----  ------
+    RSDP  bff75000      0  v00 BOCHS
+    RSDT  bff76a63     38  v01 BOCHS  BXPC     1 BXPC 1
+    FACP  bff768ff     74  v01 BOCHS  BXPC     1 BXPC 1
+    DSDT  bff75080   187f  v01 BOCHS  BXPC     1 BXPC 1
+    FACS  bff75040     40
+    APIC  bff76973     90  v01 BOCHS  BXPC     1 BXPC 1
+    HPET  bff76a03     38  v01 BOCHS  BXPC     1 BXPC 1
+    WAET  bff76a3b     28  v01 BOCHS  BXPC     1 BXPC 1
+    SSDT  bff95040     c5  v02 COREv4 COREBOOT 2a CORE 20221020
+
 
 .. _`ACPI specification`: https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf
diff --git a/doc/usage/cmd/bootflow.rst b/doc/usage/cmd/bootflow.rst
index 8590efc..a8af1f8 100644
--- a/doc/usage/cmd/bootflow.rst
+++ b/doc/usage/cmd/bootflow.rst
@@ -13,7 +13,7 @@
     bootflow select [<num|name>]
     bootflow info [-d]
     bootflow boot
-
+    bootflow cmdline [set|get|clear|delete|auto] <param> [<value>]
 
 Description
 -----------
@@ -197,6 +197,36 @@
 
 This boots the current bootflow.
 
+
+bootflow cmdline
+~~~~~~~~~~~~~~~~
+
+Some bootmeths can obtain the OS command line since it is stored with the OS.
+In that case, you can use `bootflow cmdline` to adjust this. The command line
+is assumed to be in the format used by Linux, i.e. a space-separated set of
+parameters with optional values, e.g. "noinitrd console=/dev/tty0".
+
+To change or add a parameter, use::
+
+    bootflow cmdline set <param> <value>
+
+To clear a parameter value to empty you can use "" for the value, or use::
+
+    bootflow cmdline clear <param>
+
+To delete a parameter entirely, use::
+
+    bootflow cmdline delete <param>
+
+Automatic parameters are available in a very few cases. You can use these to
+add parmeters where the value is known by U-Boot. For example::
+
+    bootflow cmdline auto earlycon
+    bootflow cmdline auto console
+
+can be used to set the early console (or console) to a suitable value so that
+output appears on the serial port. This is only supported by the 16550 serial
+driver so far.
 
 Example
 -------
@@ -258,7 +288,6 @@
     Name:      mmc@7e202000.bootdev.part_2
     Device:    mmc@7e202000.bootdev
     Block dev: mmc@7e202000.blk
-    Sequence:  1
     Method:    distro
     State:     ready
     Partition: 2
@@ -266,6 +295,10 @@
     Filename:  extlinux/extlinux.conf
     Buffer:    3db7ae88
     Size:      232 (562 bytes)
+    OS:        Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+    Cmdline:   (none)
+    Logo:      (none)
+    FDT:       <NULL>
     Error:     0
     U-Boot> bootflow boot
     ** Booting bootflow 'smsc95xx_eth.bootdev.0'
@@ -427,6 +460,69 @@
     (21 bootflows, 2 valid)
     U-Boot>
 
+Here is an example of booting ChromeOS, adjusting the console beforehand. Note that
+the cmdline is word-wrapped here and some parts of the command line are elided::
+
+    => bootfl list
+    Showing all bootflows
+    Seq  Method       State   Uclass    Part  Name                      Filename
+    ---  -----------  ------  --------  ----  ------------------------  ----------------
+    0  cros         ready   nvme         0  5.10.153-20434-g98da1eb2c <NULL>
+    1  efi          ready   nvme         c  nvme#0.blk#1.bootdev.part efi/boot/bootia32.efi
+    2  efi          ready   usb_mass_    2  usb_mass_storage.lun0.boo efi/boot/bootia32.efi
+    ---  -----------  ------  --------  ----  ------------------------  ----------------
+    (3 bootflows, 3 valid)
+    => bootfl sel 0
+    => bootfl inf
+    Name:      5.10.153-20434-g98da1eb2cf9d (chrome-bot@chromeos-release-builder-us-central1-b-x32-12-xijx) #1 SMP PREEMPT Tue Jan 24 19:38:23 PST 2023
+    Device:    nvme#0.blk#1.bootdev
+    Block dev: nvme#0.blk#1
+    Method:    cros
+    State:     ready
+    Partition: 0
+    Subdir:    (none)
+    Filename:  <NULL>
+    Buffer:    737a1400
+    Size:      c47000 (12873728 bytes)
+    OS:        ChromeOS
+    Cmdline:   console= loglevel=7 init=/sbin/init cros_secure drm.trace=0x106
+        root=/dev/dm-0 rootwait ro dm_verity.error_behavior=3
+        dm_verity.max_bios=-1 dm_verity.dev_wait=1
+        dm="1 vroot none ro 1,0 6348800
+          verity payload=PARTUUID=799c935b-ae62-d143-8493-816fa936eef7/PARTNROFF=1
+          hashtree=PARTUUID=799c935b-ae62-d143-8493-816fa936eef7/PARTNROFF=1
+          hashstart=6348800 alg=sha256
+          root_hexdigest=78cc462cd45aecbcd49ca476587b4dee59aa1b00ba5ece58e2c29ec9acd914ab
+          salt=8dec4dc80a75dd834a9b3175c674405e15b16a253fdfe05c79394ae5fd76f66a"
+        noinitrd vt.global_cursor_default=0
+        kern_guid=799c935b-ae62-d143-8493-816fa936eef7 add_efi_memmap boot=local
+        noresume noswap i915.modeset=1 ramoops.ecc=1 tpm_tis.force=0
+        intel_pmc_core.warn_on_s0ix_failures=1 i915.enable_guc=3 i915.enable_dc=4
+        xdomain=0 swiotlb=65536 intel_iommu=on i915.enable_psr=1
+        usb-storage.quirks=13fe:6500:u
+    X86 setup: 742e3400
+    Logo:      (none)
+    FDT:       <NULL>
+    Error:     0
+    => bootflow cmdline auto earlycon
+    => bootflow cmd auto console
+    => print bootargs
+    bootargs=console=ttyS0,115200n8 loglevel=7 ...
+        usb-storage.quirks=13fe:6500:u earlycon=uart8250,mmio32,0xfe03e000,115200n8
+    => bootflow cmd del console
+    => print bootargs
+    bootargs=loglevel=7 ... earlycon=uart8250,mmio32,0xfe03e000,115200n8
+    => bootfl boot
+    ** Booting bootflow '5.10.153-20434-g98da1eb2cf9d (chrome-bot@chromeos-release-builder-us-central1-b-x32-12-xijx) #1 SMP PREEMPT Tue Jan 24 19:38:23 PST 2023' with cros
+    Kernel command line: "loglevel=7 ... earlycon=uart8250,mmio32,0xfe03e000,115200n8"
+
+    Starting kernel ...
+
+    [    0.000000] Linux version 5.10.153-20434-g98da1eb2cf9d (chrome-bot@chromeos-release-builder-us-central1-b-x32-12-xijx) (Chromium OS 15.0_pre465103_p20220825-r4 clang version 15.0.0 (/var/tmp/portage/sys-devel/llvm-15.0_pre465103_p20220825-r4/work/llvm-15.0_pre465103_p20220825/clang db1978b67431ca3462ad8935bf662c15750b8252), LLD 15.0.0) #1 SMP PREEMPT Tue Jan 24 19:38:23 PST 2023
+    [    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
+
+
 
 Return value
 ------------
diff --git a/doc/usage/cmd/mtrr.rst b/doc/usage/cmd/mtrr.rst
new file mode 100644
index 0000000..531153b
--- /dev/null
+++ b/doc/usage/cmd/mtrr.rst
@@ -0,0 +1,151 @@
+.. SPDX-License-Identifier: GPL-2.0+:
+
+mtrr command
+============
+
+Synopis
+-------
+
+    mtrr [list]
+    mtrr set <reg> <type> <start> <size>
+    mtrr disable <reg>
+    mtrr enable
+
+
+Description
+-----------
+
+The *mtrr* command is used to dump the Memory Type Range Registers (MTRRs) on
+an x86 machine. These register control cache behaviour in selected memory
+ranges.
+
+Note that the number of registers can vary between CPUs.
+
+
+mtrr [list]
+~~~~~~~~~~~
+
+List the MTRRs. The table shows the following information:
+
+Reg
+    Register number (the first is register 0)
+
+Valid
+    Shows Y if the register is valid (has bit 11 set), N if not
+
+Write-type
+    Shows the behaviour when writing to the memory region. The types are
+    abbreviated to fit a reasonable line length. Valid types shown below.
+
+    ======  ==============  ====================================================
+    Value   Type            Meaning
+    ======  ==============  ====================================================
+    0       Uncacheable     Skip cache and write directly to memory
+    1       Combine         Multiple writes can be combined into one transaction
+    4       Through         Update cache and also write to memory
+    5       Protect         Writes are prohibited
+    6       Back            Update cache but don't write to memory
+    ======  ==============  ====================================================
+
+Base
+    Base memory address from which the register controls behaviour
+
+Mask
+    Mask value, which also indicates the size
+
+Size
+    Length of memory region within which the register controls behaviour
+
+
+mtrr set
+~~~~~~~~
+
+This sets the value of a particular MTRR. Parameters are:
+
+reg
+    Register number to set, with 0 being the first
+
+type
+    Access type to set. See Write-type above for valid types. This uses the name
+    rather than its numeric value.
+
+start
+    Base memory address from which the register should control behaviour
+
+size
+    Length of memory region within which the register controls behaviour
+
+
+mtrr disable
+~~~~~~~~~~~~
+
+This disables a particular register, by clearing its `valid` bit (11).
+
+
+mtrr enable
+~~~~~~~~~~~
+
+This enables a particular register, by setting its `valid` bit (11).
+
+
+Example
+-------
+
+This shows disabling and enabling an MTRR, as well as setting its type::
+
+    => mtrr
+    CPU 0:
+    Reg Valid Write-type   Base   ||        Mask   ||        Size   ||
+    0   Y     Back         0000000000000000 0000000f80000000 0000000080000000
+    1   Y     Back         0000000080000000 0000000fe0000000 0000000020000000
+    2   Y     Back         00000000a0000000 0000000ff0000000 0000000010000000
+    3   Y     Uncacheable  00000000ad000000 0000000fff000000 0000000001000000
+    4   Y     Uncacheable  00000000ae000000 0000000ffe000000 0000000002000000
+    5   Y     Combine      00000000d0000000 0000000ff0000000 0000000010000000
+    6   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    7   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    8   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    9   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    => mtrr d 5
+    => mtrr
+    CPU 0:
+    Reg Valid Write-type   Base   ||        Mask   ||        Size   ||
+    0   Y     Back         0000000000000000 0000000f80000000 0000000080000000
+    1   Y     Back         0000000080000000 0000000fe0000000 0000000020000000
+    2   Y     Back         00000000a0000000 0000000ff0000000 0000000010000000
+    3   Y     Uncacheable  00000000ad000000 0000000fff000000 0000000001000000
+    4   Y     Uncacheable  00000000ae000000 0000000ffe000000 0000000002000000
+    5   N     Combine      00000000d0000000 0000000ff0000000 0000000010000000
+    6   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    7   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    8   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    9   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    => mtrr e 5
+    => mtrr
+    CPU 0:
+    Reg Valid Write-type   Base   ||        Mask   ||        Size   ||
+    0   Y     Back         0000000000000000 0000000f80000000 0000000080000000
+    1   Y     Back         0000000080000000 0000000fe0000000 0000000020000000
+    2   Y     Back         00000000a0000000 0000000ff0000000 0000000010000000
+    3   Y     Uncacheable  00000000ad000000 0000000fff000000 0000000001000000
+    4   Y     Uncacheable  00000000ae000000 0000000ffe000000 0000000002000000
+    5   Y     Combine      00000000d0000000 0000000ff0000000 0000000010000000
+    6   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    7   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    8   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    9   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    => mtrr set 5 Uncacheable d0000000 10000000
+    => mtrr
+    CPU 0:
+    Reg Valid Write-type   Base   ||        Mask   ||        Size   ||
+    0   Y     Back         0000000000000000 0000000f80000000 0000000080000000
+    1   Y     Back         0000000080000000 0000000fe0000000 0000000020000000
+    2   Y     Back         00000000a0000000 0000000ff0000000 0000000010000000
+    3   Y     Uncacheable  00000000ad000000 0000000fff000000 0000000001000000
+    4   Y     Uncacheable  00000000ae000000 0000000ffe000000 0000000002000000
+    5   Y     Uncacheable  00000000d0000000 0000000ff0000000 0000000010000000
+    6   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    7   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    8   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    9   N     Uncacheable  0000000000000000 0000000000000000 0000001000000000
+    =>
diff --git a/doc/usage/cmd/part.rst b/doc/usage/cmd/part.rst
index 8d2a280..8a594aa 100644
--- a/doc/usage/cmd/part.rst
+++ b/doc/usage/cmd/part.rst
@@ -13,6 +13,7 @@
     part start <interface> <dev> <part> <varname>
     part size <interface> <dev> <part> <varname>
     part number <interface> <dev> <part> <varname>
+    part set <interface> <dev> <part> <type>
     part type <interface> <dev>:<part> [varname]
     part types
 
@@ -82,6 +83,18 @@
     varname
         a variable to store the current partition number value into
 
+The 'part set' command sets the type of a partition. This is useful when
+autodetection fails or does not do the correct thing:
+
+    interface
+        interface for accessing the block device (mmc, sata, scsi, usb, ....)
+    dev
+        device number
+    part
+        partition number
+    type
+        partition type to use (see 'part types') to check available types
+
 The 'part type' command prints or sets an environment variable to the partition type UUID.
 
     interface
@@ -147,6 +160,67 @@
     => part types
     Supported partition tables: EFI, AMIGA, DOS, ISO, MAC
 
+This shows looking at a device with multiple partition tables::
+
+    => virtio scan
+    => part list virtio 0
+
+    Partition Map for VirtIO device 0  --   Partition Type: EFI
+
+    Part	Start LBA	End LBA		Name
+            Attributes
+            Type GUID
+            Partition GUID
+    1	0x00000040	0x0092b093	"ISO9660"
+            attrs:	0x1000000000000001
+            type:	ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
+            guid:	a0891d7e-b930-4513-94d8-f629dbd637b2
+    2	0x0092b094	0x0092d7e7	"Appended2"
+            attrs:	0x0000000000000000
+            type:	c12a7328-f81f-11d2-ba4b-00a0c93ec93b
+            guid:	a0891d7e-b930-4513-94db-f629dbd637b2
+    3	0x0092d7e8	0x0092da3f	"Gap1"
+            attrs:	0x1000000000000001
+            type:	ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
+            guid:	a0891d7e-b930-4513-94da-f629dbd637b2
+    => ls virtio 0:3
+    => part types
+    Supported partition tables: EFI, DOS, ISO
+    => part set virtio 0 dos
+
+    Partition Map for VirtIO device 0  --   Partition Type: DOS
+
+    Part	Start Sector	Num Sectors	UUID		Type
+    1	1         	9624191   	00000000-01	ee
+    => part set virtio 0 iso
+
+    Partition Map for VirtIO device 0  --   Partition Type: ISO
+
+    Part   Start     Sect x Size Type
+    1     3020        4    512 U-Boot
+    2  9613460    10068    512 U-Boot
+    => part set virtio 0 efi
+
+    Partition Map for VirtIO device 0  --   Partition Type: EFI
+
+    Part	Start LBA	End LBA		Name
+            Attributes
+            Type GUID
+            Partition GUID
+    1	0x00000040	0x0092b093	"ISO9660"
+            attrs:	0x1000000000000001
+            type:	ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
+            guid:	a0891d7e-b930-4513-94d8-f629dbd637b2
+    2	0x0092b094	0x0092d7e7	"Appended2"
+            attrs:	0x0000000000000000
+            type:	c12a7328-f81f-11d2-ba4b-00a0c93ec93b
+            guid:	a0891d7e-b930-4513-94db-f629dbd637b2
+    3	0x0092d7e8	0x0092da3f	"Gap1"
+            attrs:	0x1000000000000001
+            type:	ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
+            guid:	a0891d7e-b930-4513-94da-f629dbd637b2
+    =>
+
 Return value
 ------------
 
diff --git a/doc/usage/cmd/qfw.rst b/doc/usage/cmd/qfw.rst
index cc0e27c..ec13e09 100644
--- a/doc/usage/cmd/qfw.rst
+++ b/doc/usage/cmd/qfw.rst
@@ -41,18 +41,21 @@
 ::
 
     => qfw list
-    etc/boot-fail-wait
-    etc/smbios/smbios-tables
-    etc/smbios/smbios-anchor
-    etc/e820
-    genroms/kvmvapic.bin
-    genroms/linuxboot.bin
-    etc/system-states
-    etc/acpi/tables
-    etc/table-loader
-    etc/tpm/log
-    etc/acpi/rsdp
-    bootorder
+    00000000 bios-geometry
+    00000000 bootorder
+    000f0060 etc/acpi/rsdp
+    bed14040 etc/acpi/tables
+    00000000 etc/boot-fail-wait
+    00000000 etc/e820
+    00000000 etc/smbios/smbios-anchor
+    00000000 etc/smbios/smbios-tables
+    00000000 etc/system-states
+    00000000 etc/table-loader
+    00000000 etc/tpm/log
+    00000000 genroms/kvmvapic.bin
+
+Where an address is shown, it indicates where the data is available for
+inspection, e.g. using the :doc:`md`.
 
 The available CPUs can be shown via the *qfw cpus* command:
 
diff --git a/doc/usage/index.rst b/doc/usage/index.rst
index f2ffd27..072db53 100644
--- a/doc/usage/index.rst
+++ b/doc/usage/index.rst
@@ -76,6 +76,7 @@
    cmd/md
    cmd/mmc
    cmd/mtest
+   cmd/mtrr
    cmd/panic
    cmd/part
    cmd/pause
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 75937fb..a25f6ae 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -10,6 +10,8 @@
 
 source "drivers/axi/Kconfig"
 
+source "drivers/bios_emulator/Kconfig"
+
 source "drivers/bus/Kconfig"
 
 source "drivers/block/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 78dcf62..3bc6d27 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0+
 
+obj-$(CONFIG_$(SPL_TPL_)BIOSEMU) += bios_emulator/
 obj-$(CONFIG_$(SPL_TPL_)BLK) += block/
 obj-$(CONFIG_$(SPL_TPL_)BOOTCOUNT_LIMIT) += bootcount/
 obj-$(CONFIG_$(SPL_TPL_)BUTTON) += button/
@@ -80,7 +81,6 @@
 obj-y += adc/
 obj-y += ata/
 obj-$(CONFIG_DM_DEMO) += demo/
-obj-$(CONFIG_BIOSEMU) += bios_emulator/
 obj-y += block/
 obj-y += cache/
 obj-$(CONFIG_CPU) += cpu/
diff --git a/drivers/bios_emulator/Kconfig b/drivers/bios_emulator/Kconfig
new file mode 100644
index 0000000..3660576
--- /dev/null
+++ b/drivers/bios_emulator/Kconfig
@@ -0,0 +1,10 @@
+config BIOSEMU
+	bool
+	select X86EMU_RAW_IO
+
+config SPL_BIOSEMU
+	bool
+	select X86EMU_RAW_IO
+
+config X86EMU_RAW_IO
+	bool
diff --git a/drivers/bios_emulator/biosemui.h b/drivers/bios_emulator/biosemui.h
index 7853015..954cd88 100644
--- a/drivers/bios_emulator/biosemui.h
+++ b/drivers/bios_emulator/biosemui.h
@@ -128,19 +128,19 @@
 	u32 finalVal;
 } BE_portInfo;
 
-#define PM_inpb(port)	inb(port+VIDEO_IO_OFFSET)
-#define PM_inpw(port)	inw(port+VIDEO_IO_OFFSET)
-#define PM_inpd(port)	inl(port+VIDEO_IO_OFFSET)
-#define PM_outpb(port,val)	outb(val,port+VIDEO_IO_OFFSET)
-#define PM_outpw(port,val)	outw(val,port+VIDEO_IO_OFFSET)
-#define PM_outpd(port,val)	outl(val,port+VIDEO_IO_OFFSET)
+#define PM_inpb(port)	inb(port)
+#define PM_inpw(port)	inw(port)
+#define PM_inpd(port)	inl(port)
+#define PM_outpb(port, val)	outb(val, port)
+#define PM_outpw(port, val)	outw(val, port)
+#define PM_outpd(port, val)	outl(val, port)
 
 #define LOG_inpb(port)	PM_inpb(port)
 #define LOG_inpw(port)	PM_inpw(port)
 #define LOG_inpd(port)	PM_inpd(port)
-#define LOG_outpb(port,val)	PM_outpb(port,val)
-#define LOG_outpw(port,val)	PM_outpw(port,val)
-#define LOG_outpd(port,val)	PM_outpd(port,val)
+#define LOG_outpb(port, val)	PM_outpb(port, val)
+#define LOG_outpw(port, val)	PM_outpw(port, val)
+#define LOG_outpd(port, val)	PM_outpd(port, val)
 
 /*-------------------------- Function Prototypes --------------------------*/
 
diff --git a/drivers/bios_emulator/x86emu/sys.c b/drivers/bios_emulator/x86emu/sys.c
index c2db121..882a8a3 100644
--- a/drivers/bios_emulator/x86emu/sys.c
+++ b/drivers/bios_emulator/x86emu/sys.c
@@ -44,6 +44,7 @@
 
 /*------------------------- Global Variables ------------------------------*/
 
+/* Note: bios.c defines this if the emulator is not enabled */
 X86EMU_sysEnv _X86EMU_env;	/* Global emulator machine state */
 X86EMU_intrFuncs _X86EMU_intrTab[256];
 
diff --git a/drivers/gpio/intel_ich6_gpio.c b/drivers/gpio/intel_ich6_gpio.c
index 63a07b9..2ed0d0b 100644
--- a/drivers/gpio/intel_ich6_gpio.c
+++ b/drivers/gpio/intel_ich6_gpio.c
@@ -26,6 +26,8 @@
  * reserved or subject to arcane restrictions.
  */
 
+#define LOG_CATEGORY	UCLASS_GPIO
+
 #include <common.h>
 #include <dm.h>
 #include <errno.h>
@@ -155,8 +157,7 @@
 	 */
 	tmplong = inl(bank->use_sel);
 	if (!(tmplong & (1UL << offset))) {
-		debug("%s: gpio %d is reserved for internal use\n", __func__,
-		      offset);
+		log_debug("gpio %d is reserved for internal use\n", offset);
 		return -EPERM;
 	}
 
diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c
index 9ef95ca..7c01bf2 100644
--- a/drivers/misc/qfw.c
+++ b/drivers/misc/qfw.c
@@ -18,6 +18,7 @@
 #include <dm.h>
 #include <misc.h>
 #include <tables_csum.h>
+#include <asm/acpi_table.h>
 
 #if defined(CONFIG_GENERATE_ACPI_TABLE) && !defined(CONFIG_SANDBOX)
 /*
@@ -64,6 +65,11 @@
 			printf("error: allocating resource\n");
 			return -ENOMEM;
 		}
+		if (aligned_addr < gd->arch.table_start_high)
+			gd->arch.table_start_high = aligned_addr;
+		if (aligned_addr + size > gd->arch.table_end_high)
+			gd->arch.table_end_high = aligned_addr + size;
+
 	} else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) {
 		aligned_addr = ALIGN(*addr, align);
 	} else {
@@ -188,6 +194,10 @@
 		return addr;
 	}
 
+	/* QFW always puts tables at high addresses */
+	gd->arch.table_start_high = (ulong)table_loader;
+	gd->arch.table_end_high = (ulong)table_loader;
+
 	qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader);
 
 	for (i = 0; i < (size / sizeof(*entry)); i++) {
@@ -227,6 +237,9 @@
 	}
 
 	free(table_loader);
+
+	gd_set_acpi_start(acpi_get_rsdp_addr());
+
 	return addr;
 }
 
diff --git a/drivers/mmc/mmc_bootdev.c b/drivers/mmc/mmc_bootdev.c
index b57b8a6..55ecead 100644
--- a/drivers/mmc/mmc_bootdev.c
+++ b/drivers/mmc/mmc_bootdev.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Bootdevice for MMC
+ * Bootdev for MMC
  *
  * Copyright 2021 Google LLC
  * Written by Simon Glass <sjg@chromium.org>
diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c
index 74e7a5b..a7add66 100644
--- a/drivers/nvme/nvme.c
+++ b/drivers/nvme/nvme.c
@@ -578,17 +578,22 @@
 	return min(result & 0xffff, result >> 16) + 1;
 }
 
-static void nvme_create_io_queues(struct nvme_dev *dev)
+static int nvme_create_io_queues(struct nvme_dev *dev)
 {
 	unsigned int i;
+	int ret;
 
 	for (i = dev->queue_count; i <= dev->max_qid; i++)
 		if (!nvme_alloc_queue(dev, i, dev->q_depth))
-			break;
+			return log_msg_ret("all", -ENOMEM);
 
-	for (i = dev->online_queues; i <= dev->queue_count - 1; i++)
-		if (nvme_create_queue(dev->queues[i], i))
-			break;
+	for (i = dev->online_queues; i <= dev->queue_count - 1; i++) {
+		ret = nvme_create_queue(dev->queues[i], i);
+		if (ret)
+			return log_msg_ret("cre", ret);
+	}
+
+	return 0;
 }
 
 static int nvme_setup_io_queues(struct nvme_dev *dev)
@@ -598,14 +603,18 @@
 
 	nr_io_queues = 1;
 	result = nvme_set_queue_count(dev, nr_io_queues);
-	if (result <= 0)
+	if (result <= 0) {
+		log_debug("Cannot set queue count (err=%dE)\n", result);
 		return result;
+	}
 
 	dev->max_qid = nr_io_queues;
 
 	/* Free previously allocated queues */
 	nvme_free_queues(dev, nr_io_queues + 1);
-	nvme_create_io_queues(dev);
+	result = nvme_create_io_queues(dev);
+	if (result)
+		return result;
 
 	return 0;
 }
@@ -683,8 +692,11 @@
 
 	uclass_foreach_dev(dev, uc) {
 		ret = device_probe(dev);
-		if (ret)
+		if (ret) {
+			log_err("Failed to probe '%s': err=%dE\n", dev->name,
+				ret);
 			return ret;
+		}
 	}
 
 	return 0;
@@ -842,8 +854,10 @@
 	ndev->dbs = ((void __iomem *)ndev->bar) + 4096;
 
 	ret = nvme_configure_admin_queue(ndev);
-	if (ret)
+	if (ret) {
+		log_debug("Unable to configure admin queue (err=%dE)\n", ret);
 		goto free_queue;
+	}
 
 	/* Allocate after the page size is known */
 	ndev->prp_pool = memalign(ndev->page_size, MAX_PRP_POOL);
@@ -855,8 +869,10 @@
 	ndev->prp_entry_num = MAX_PRP_POOL >> 3;
 
 	ret = nvme_setup_io_queues(ndev);
-	if (ret)
+	if (ret) {
+		log_debug("Unable to setup I/O queues(err=%dE)\n", ret);
 		goto free_queue;
+	}
 
 	nvme_get_info_from_identify(ndev);
 
diff --git a/drivers/pch/pch9.c b/drivers/pch/pch9.c
index 3bd0115..3137eb2 100644
--- a/drivers/pch/pch9.c
+++ b/drivers/pch/pch9.c
@@ -3,6 +3,8 @@
  * Copyright (C) 2014 Google, Inc
  */
 
+#define LOG_CATEGORY	UCLASS_PCH
+
 #include <common.h>
 #include <dm.h>
 #include <log.h>
@@ -38,7 +40,7 @@
 	 */
 	dm_pci_read_config32(dev, GPIO_BASE, &base);
 	if (base == 0x00000000 || base == 0xffffffff) {
-		debug("%s: unexpected BASE value\n", __func__);
+		log_debug("unexpected BASE value\n");
 		return -ENODEV;
 	}
 
@@ -59,7 +61,7 @@
 
 	dm_pci_read_config32(dev, IO_BASE, &base);
 	if (base == 0x00000000 || base == 0xffffffff) {
-		debug("%s: unexpected BASE value\n", __func__);
+		log_debug("unexpected BASE value\n");
 		return -ENODEV;
 	}
 
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 84a2ae9..aca439d 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -44,8 +44,13 @@
 	bool "Enable Plug & Play support for PCI"
 	help
 	  Enable PCI memory and I/O space resource allocation and assignment.
+
 	  This is required to auto configure the enumerated devices.
 
+	  This is normally not done in SPL, but can be enabled if devices must
+	  be set up in the SPL phase. Often it is enough to manually configure
+	  one device, so this option can be disabled.
+
 config PCI_REGION_MULTI_ENTRY
 	bool "Enable Multiple entries of region type MEMORY in ranges for PCI"
 	help
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
index 8d27e40..632c1a6 100644
--- a/drivers/pci/pci-uclass.c
+++ b/drivers/pci/pci-uclass.c
@@ -13,6 +13,7 @@
 #include <log.h>
 #include <malloc.h>
 #include <pci.h>
+#include <spl.h>
 #include <asm/global_data.h>
 #include <asm/io.h>
 #include <dm/device-internal.h>
@@ -722,6 +723,9 @@
 	u32 vendev;
 	int index;
 
+	if (spl_phase() == PHASE_SPL && CONFIG_IS_ENABLED(PCI_PNP))
+		return true;
+
 	for (index = 0;
 	     !dev_read_u32_index(bus, "u-boot,pci-pre-reloc", index,
 				 &vendev);
@@ -793,7 +797,9 @@
 			 * space is pretty limited (ie: using Cache As RAM).
 			 */
 			if (!(gd->flags & GD_FLG_RELOC) &&
-			    !(drv->flags & DM_FLAG_PRE_RELOC))
+			    !(drv->flags & DM_FLAG_PRE_RELOC) &&
+			    (!CONFIG_IS_ENABLED(PCI_PNP) ||
+			     spl_phase() != PHASE_SPL))
 				return log_msg_ret("pre", -EPERM);
 
 			/*
@@ -918,6 +924,8 @@
 			}
 			ret = pci_find_and_bind_driver(bus, &find_id, bdf,
 						       &dev);
+		} else {
+			debug("device: %s\n", dev->name);
 		}
 		if (ret == -EPERM)
 			continue;
diff --git a/drivers/pci/pci_rom.c b/drivers/pci/pci_rom.c
index f0dfe63..438583a 100644
--- a/drivers/pci/pci_rom.c
+++ b/drivers/pci/pci_rom.c
@@ -26,6 +26,7 @@
 
 #include <common.h>
 #include <bios_emul.h>
+#include <bloblist.h>
 #include <bootstage.h>
 #include <dm.h>
 #include <errno.h>
@@ -34,6 +35,7 @@
 #include <malloc.h>
 #include <pci.h>
 #include <pci_rom.h>
+#include <spl.h>
 #include <vesa.h>
 #include <video.h>
 #include <acpi/acpi_s3.h>
@@ -91,6 +93,7 @@
 		debug("%s: rom_address=%x\n", __func__, rom_address);
 		return -ENOENT;
 	}
+	rom_address &= PCI_ROM_ADDRESS_MASK;
 
 	/* Enable expansion ROM address decoding. */
 	dm_pci_write_config32(dev, PCI_ROM_ADDRESS,
@@ -254,14 +257,16 @@
 
 	ret = pci_rom_probe(dev, &rom);
 	if (ret)
-		return ret;
+		return log_msg_ret("pro", ret);
 
 	ret = pci_rom_load(rom, &ram, &alloced);
-	if (ret)
+	if (ret) {
+		ret = log_msg_ret("ld", ret);
 		goto err;
+	}
 
 	if (!board_should_run_oprom(dev)) {
-		ret = -ENXIO;
+		ret = log_msg_ret("run", -ENXIO);
 		goto err;
 	}
 
@@ -269,7 +274,7 @@
 		defined(CONFIG_FRAMEBUFFER_VESA_MODE)
 	vesa_mode = CONFIG_FRAMEBUFFER_VESA_MODE;
 #endif
-	debug("Selected vesa mode %#x\n", vesa_mode);
+	debug("Selected vesa mode 0x%x\n", vesa_mode);
 
 	if (exec_method & PCI_ROM_USE_NATIVE) {
 #ifdef CONFIG_X86
@@ -296,27 +301,31 @@
 	}
 
 	if (emulate) {
-#ifdef CONFIG_BIOSEMU
-		BE_VGAInfo *info;
+		if (CONFIG_IS_ENABLED(BIOSEMU)) {
+			BE_VGAInfo *info;
 
-		ret = biosemu_setup(dev, &info);
-		if (ret)
-			goto err;
-		biosemu_set_interrupt_handler(0x15, int15_handler);
-		ret = biosemu_run(dev, (uchar *)ram, 1 << 16, info,
-				  true, vesa_mode, &mode_info);
-		if (ret)
-			goto err;
-#endif
+			log_debug("Running video BIOS with emulator...");
+			ret = biosemu_setup(dev, &info);
+			if (ret)
+				goto err;
+			biosemu_set_interrupt_handler(0x15, int15_handler);
+			ret = biosemu_run(dev, (uchar *)ram, 1 << 16, info,
+					  true, vesa_mode, &mode_info);
+			log_debug("done\n");
+			if (ret)
+				goto err;
+		}
 	} else {
 #if defined(CONFIG_X86) && (CONFIG_IS_ENABLED(X86_32BIT_INIT) || CONFIG_TPL)
+		log_debug("Running video BIOS...");
 		bios_set_interrupt_handler(0x15, int15_handler);
 
 		bios_run_on_x86(dev, (unsigned long)ram, vesa_mode,
 				&mode_info);
+		log_debug("done\n");
 #endif
 	}
-	debug("Final vesa mode %#x\n", mode_info.video_mode);
+	debug("Final vesa mode %x\n", mode_info.video_mode);
 	ret = 0;
 
 err:
@@ -368,34 +377,68 @@
 		printf("Not available (previous bootloader prevents it)\n");
 		return -EPERM;
 	}
-	bootstage_start(BOOTSTAGE_ID_ACCUM_LCD, "vesa display");
-	ret = dm_pci_run_vga_bios(dev, int15_handler, PCI_ROM_USE_NATIVE |
-					PCI_ROM_ALLOW_FALLBACK);
-	bootstage_accum(BOOTSTAGE_ID_ACCUM_LCD);
-	if (ret) {
-		debug("failed to run video BIOS: %d\n", ret);
-		return ret;
-	}
 
-	ret = vesa_setup_video_priv(&mode_info.vesa,
-				    mode_info.vesa.phys_base_ptr, uc_priv,
-				    plat);
-	if (ret) {
-		if (ret == -ENFILE) {
-			/*
-			 * See video-uclass.c for how to set up reserved memory
-			 * in your video driver
-			 */
-			log_err("CONFIG_VIDEO_COPY enabled but driver '%s' set up no reserved memory\n",
-				dev->driver->name);
+	/* In U-Boot proper, collect the information added by SPL (see below) */
+	if (IS_ENABLED(CONFIG_SPL_VIDEO) && spl_phase() > PHASE_SPL &&
+	    CONFIG_IS_ENABLED(BLOBLIST)) {
+		struct video_handoff *ho;
+
+		ho = bloblist_find(BLOBLISTT_U_BOOT_VIDEO, sizeof(*ho));
+		if (!ho)
+			return log_msg_ret("blf", -ENOENT);
+		plat->base = ho->fb;
+		plat->size = ho->size;
+		uc_priv->xsize = ho->xsize;
+		uc_priv->ysize = ho->ysize;
+		uc_priv->line_length = ho->line_length;
+		uc_priv->bpix = ho->bpix;
+	} else {
+		bootstage_start(BOOTSTAGE_ID_ACCUM_LCD, "vesa display");
+		ret = dm_pci_run_vga_bios(dev, int15_handler,
+					  PCI_ROM_USE_NATIVE |
+					  PCI_ROM_ALLOW_FALLBACK);
+		bootstage_accum(BOOTSTAGE_ID_ACCUM_LCD);
+		if (ret) {
+			debug("failed to run video BIOS: %d\n", ret);
+			return ret;
 		}
 
-		debug("No video mode configured\n");
-		return ret;
+		ret = vesa_setup_video_priv(&mode_info.vesa,
+					    mode_info.vesa.phys_base_ptr,
+					    uc_priv, plat);
+		if (ret) {
+			if (ret == -ENFILE) {
+				/*
+				 * See video-uclass.c for how to set up reserved
+				 * memory in your video driver
+				 */
+				log_err("CONFIG_VIDEO_COPY enabled but driver '%s' set up no reserved memory\n",
+					dev->driver->name);
+			}
+
+			debug("No video mode configured\n");
+			return ret;
+		}
 	}
 
 	printf("Video: %dx%dx%d\n", uc_priv->xsize, uc_priv->ysize,
 	       mode_info.vesa.bits_per_pixel);
 
+	/* In SPL, store the information for use by U-Boot proper */
+	if (spl_phase() == PHASE_SPL && CONFIG_IS_ENABLED(BLOBLIST)) {
+		struct video_handoff *ho;
+
+		ho = bloblist_add(BLOBLISTT_U_BOOT_VIDEO, sizeof(*ho), 0);
+		if (!ho)
+			return log_msg_ret("blc", -ENOMEM);
+
+		ho->fb = plat->base;
+		ho->size = plat->size;
+		ho->xsize = uc_priv->xsize;
+		ho->ysize = uc_priv->ysize;
+		ho->line_length = uc_priv->line_length;
+		ho->bpix = uc_priv->bpix;
+	}
+
 	return 0;
 }
diff --git a/drivers/scsi/scsi_bootdev.c b/drivers/scsi/scsi_bootdev.c
index 991013f..218221f 100644
--- a/drivers/scsi/scsi_bootdev.c
+++ b/drivers/scsi/scsi_bootdev.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Bootdevice for USB
+ * Bootdev for SCSI
  *
  * Copyright 2021 Google LLC
  * Written by Simon Glass <sjg@chromium.org>
diff --git a/drivers/usb/host/usb_bootdev.c b/drivers/usb/host/usb_bootdev.c
index 06e8f61..7fa1c60 100644
--- a/drivers/usb/host/usb_bootdev.c
+++ b/drivers/usb/host/usb_bootdev.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Bootdevice for USB
+ * Bootdev for USB
  *
  * Copyright 2021 Google LLC
  * Written by Simon Glass <sjg@chromium.org>
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index b209cb7..b8147f2 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -278,6 +278,36 @@
 	  possible to update the environment, the breakage may be confusing for
 	  users. This option will be removed around the end of 2020.
 
+config VIDEO_BOCHS
+	bool "Enable Bochs video emulation for QEMU"
+	depends on X86
+	help
+	  Enable this to use the Bochs video support provided in the QEMU
+	  emulator. This appears as a PCI device which U-Boot can set up to
+	  provide a frame buffer.
+
+if VIDEO_BOCHS
+
+config VIDEO_BOCHS_SIZE_X
+	int "Width of display (X resolution)"
+	default 1280
+	help
+	  Sets the width of the display.
+
+	  These two options control the size of the display set up by QEMU.
+	  Typical sizes are 1024 x 768 or 1280 x 1024.
+
+config VIDEO_BOCHS_SIZE_Y
+	int "High of display (Y resolution)"
+	default 1024
+	help
+	  Sets the height of the display.
+
+	  These two options control the size of the display set up by QEMU.
+	  Typical sizes are 1024 x 768 or 1280 x 1024.
+
+endif
+
 config VIDEO_COREBOOT
 	bool "Enable coreboot framebuffer driver support"
 	depends on X86
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index d710c1f..d13af9f 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -45,6 +45,7 @@
 obj-$(CONFIG_SANDBOX_OSD) += sandbox_osd.o
 obj-$(CONFIG_VIDEO_ARM_MALIDP) += mali_dp.o
 obj-$(CONFIG_VIDEO_BCM2835) += bcm2835.o
+obj-$(CONFIG_VIDEO_BOCHS) += bochs.o
 obj-$(CONFIG_VIDEO_BROADWELL_IGD) += broadwell_igd.o
 obj-$(CONFIG_VIDEO_COREBOOT) += coreboot.o
 obj-$(CONFIG_VIDEO_DW_HDMI) += dw_hdmi.o
diff --git a/drivers/video/bochs.c b/drivers/video/bochs.c
new file mode 100644
index 0000000..2136b51
--- /dev/null
+++ b/drivers/video/bochs.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Modified from coreboot bochs.c
+ */
+
+#define LOG_CATEGORY	UCLASS_VIDEO
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <pci.h>
+#include <video.h>
+#include <asm/io.h>
+#include <asm/mtrr.h>
+#include <linux/sizes.h>
+#include "bochs.h"
+
+static int xsize = CONFIG_VIDEO_BOCHS_SIZE_X;
+static int ysize = CONFIG_VIDEO_BOCHS_SIZE_Y;
+
+static void bochs_write(void *mmio, int index, int val)
+{
+	writew(val, mmio + MMIO_BASE + index * 2);
+}
+
+static int bochs_read(void *mmio, int index)
+{
+	return readw(mmio + MMIO_BASE + index * 2);
+}
+
+static void bochs_vga_write(int index, uint8_t val)
+{
+	outb(val, VGA_INDEX);
+}
+
+static int bochs_init_fb(struct udevice *dev)
+{
+	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
+	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+	ulong fb;
+	void *mmio;
+	int id, mem;
+
+	log_debug("probing %s at PCI %x\n", dev->name, dm_pci_get_bdf(dev));
+	fb = dm_pci_read_bar32(dev, 0);
+	if (!fb)
+		return log_msg_ret("fb", -EIO);
+
+	/* MMIO bar supported since qemu 3.0+ */
+	mmio = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_2, 0, 0, PCI_REGION_TYPE,
+			      PCI_REGION_MEM);
+
+	if (!mmio)
+		return log_msg_ret("map", -EIO);
+
+	/* bochs dispi detection */
+	id = bochs_read(mmio, INDEX_ID);
+	if ((id & 0xfff0) != ID0) {
+		log_debug("ID mismatch\n");
+		return -EPROTONOSUPPORT;
+	}
+	mem = bochs_read(mmio, INDEX_VIDEO_MEMORY_64K) * SZ_64K;
+	log_debug("QEMU VGA: bochs @ %p: %d MiB FB at %lx\n", mmio, mem / SZ_1M,
+		  fb);
+
+	uc_priv->xsize = xsize;
+	uc_priv->ysize = ysize;
+	uc_priv->bpix = VIDEO_BPP32;
+
+	/* setup video mode */
+	bochs_write(mmio, INDEX_ENABLE,  0);
+	bochs_write(mmio, INDEX_BANK,  0);
+	bochs_write(mmio, INDEX_BPP, VNBITS(uc_priv->bpix));
+	bochs_write(mmio, INDEX_XRES, xsize);
+	bochs_write(mmio, INDEX_YRES, ysize);
+	bochs_write(mmio, INDEX_VIRT_WIDTH, xsize);
+	bochs_write(mmio, INDEX_VIRT_HEIGHT, ysize);
+	bochs_write(mmio, INDEX_X_OFFSET, 0);
+	bochs_write(mmio, INDEX_Y_OFFSET, 0);
+	bochs_write(mmio, INDEX_ENABLE, ENABLED | LFB_ENABLED);
+
+	bochs_vga_write(0, 0x20);	/* disable blanking */
+
+	plat->base = fb;
+
+	return 0;
+}
+
+static int bochs_video_probe(struct udevice *dev)
+{
+	int ret;
+
+	ret = bochs_init_fb(dev);
+	if (ret)
+		return log_ret(ret);
+
+	return 0;
+}
+
+static int bochs_video_bind(struct udevice *dev)
+{
+	struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
+
+	/* Set the maximum supported resolution */
+	uc_plat->size = 2560 * 1600 * 4;
+	log_debug("%s: Frame buffer size %x\n", __func__, uc_plat->size);
+
+	return 0;
+}
+
+U_BOOT_DRIVER(bochs_video) = {
+	.name	= "bochs_video",
+	.id	= UCLASS_VIDEO,
+	.bind	= bochs_video_bind,
+	.probe	= bochs_video_probe,
+};
+
+static struct pci_device_id bochs_video_supported[] = {
+	{ PCI_DEVICE(0x1234, 0x1111) },
+	{ },
+};
+
+U_BOOT_PCI_DEVICE(bochs_video, bochs_video_supported);
diff --git a/drivers/video/bochs.h b/drivers/video/bochs.h
new file mode 100644
index 0000000..4c8ec83
--- /dev/null
+++ b/drivers/video/bochs.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Modified from coreboot bochs.c
+ */
+
+#ifndef __BOCHS_H
+#define __BOCHS_H
+
+#define VGA_INDEX	0x3c0
+
+#define IOPORT_INDEX	0x01ce
+#define IOPORT_DATA	0x01cf
+
+enum {
+	INDEX_ID,
+	INDEX_XRES,
+	INDEX_YRES,
+	INDEX_BPP,
+	INDEX_ENABLE,
+	INDEX_BANK,
+	INDEX_VIRT_WIDTH,
+	INDEX_VIRT_HEIGHT,
+	INDEX_X_OFFSET,
+	INDEX_Y_OFFSET,
+	INDEX_VIDEO_MEMORY_64K
+};
+
+#define ID0		0xb0c0
+
+#define ENABLED		BIT(0)
+#define LFB_ENABLED	BIT(6)
+#define NOCLEARMEM	BIT(7)
+
+#define MMIO_BASE	0x500
+
+#endif
diff --git a/fs/fat/fat.c b/fs/fat/fat.c
index 2da93da..d1476aa 100644
--- a/fs/fat/fat.c
+++ b/fs/fat/fat.c
@@ -8,6 +8,8 @@
  * 2003-03-10 - kharris@nexus-tech.net - ported to uboot
  */
 
+#define LOG_CATEGORY	LOGC_FS
+
 #include <common.h>
 #include <blk.h>
 #include <config.h>
@@ -97,8 +99,8 @@
 	/* Read the partition table, if present */
 	if (part_get_info(dev_desc, part_no, &info)) {
 		if (part_no != 0) {
-			printf("** Partition %d not valid on device %d **\n",
-					part_no, dev_desc->devnum);
+			log_err("Partition %d invalid on device %d\n", part_no,
+				dev_desc->devnum);
 			return -1;
 		}
 
@@ -168,7 +170,7 @@
 	__u32 ret = 0x00;
 
 	if (CHECK_CLUST(entry, mydata->fatsize)) {
-		printf("Error: Invalid FAT entry: 0x%08x\n", entry);
+		log_err("Invalid FAT entry: %#08x\n", entry);
 		return ret;
 	}
 
@@ -586,19 +588,19 @@
 	mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
 	mydata->clust_size = bs.cluster_size;
 	if (mydata->sect_size != cur_part_info.blksz) {
-		printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n",
-				mydata->sect_size, cur_part_info.blksz);
+		log_err("FAT sector size mismatch (fs=%u, dev=%lu)\n",
+			mydata->sect_size, cur_part_info.blksz);
 		return -1;
 	}
 	if (mydata->clust_size == 0) {
-		printf("Error: FAT cluster size not set\n");
+		log_err("FAT cluster size not set\n");
 		return -1;
 	}
 	if ((unsigned int)mydata->clust_size * mydata->sect_size >
 	    MAX_CLUSTSIZE) {
-		printf("Error: FAT cluster size too big (cs=%u, max=%u)\n",
-		       (unsigned int)mydata->clust_size * mydata->sect_size,
-		       MAX_CLUSTSIZE);
+		log_err("FAT cluster size too big (cs=%u, max=%u)\n",
+			(uint)mydata->clust_size * mydata->sect_size,
+			MAX_CLUSTSIZE);
 		return -1;
 	}
 
diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
index 413fc43..e2a9913 100644
--- a/fs/fat/fat_write.c
+++ b/fs/fat/fat_write.c
@@ -1571,8 +1571,9 @@
 	char *filename_copy, *dirname, *basename;
 
 	filename_copy = strdup(filename);
-	if (!filename_copy) {
-		printf("Error: allocating memory\n");
+	itr = malloc_cache_aligned(sizeof(fat_itr));
+	if (!itr || !filename_copy) {
+		printf("Error: out of memory\n");
 		ret = -ENOMEM;
 		goto exit;
 	}
@@ -1584,13 +1585,6 @@
 		goto exit;
 	}
 
-	itr = malloc_cache_aligned(sizeof(fat_itr));
-	if (!itr) {
-		printf("Error: allocating memory\n");
-		ret = -ENOMEM;
-		goto exit;
-	}
-
 	ret = fat_itr_root(itr, &fsdata);
 	if (ret)
 		goto exit;
@@ -1605,7 +1599,7 @@
 	}
 
 	if (!find_directory_entry(itr, basename)) {
-		printf("%s: doesn't exist\n", basename);
+		log_err("%s: doesn't exist (%d)\n", basename, -ENOENT);
 		ret = -ENOENT;
 		goto exit;
 	}
diff --git a/fs/sandbox/host_bootdev.c b/fs/sandbox/host_bootdev.c
index 0d12ee4..3ef5362 100644
--- a/fs/sandbox/host_bootdev.c
+++ b/fs/sandbox/host_bootdev.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Bootdevice for MMC
+ * Bootdev for sandbox host
  *
  * Copyright 2021 Google LLC
  * Written by Simon Glass <sjg@chromium.org>
diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h
index a1e1b9d..8fc205d 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -301,6 +301,12 @@
 	 * @timebase_l: low 32 bits of timer
 	 */
 	unsigned int timebase_l;
+	/**
+	 * @malloc_start: start of malloc() region
+	 */
+#if CONFIG_IS_ENABLED(CMD_BDINFO_EXTRA)
+	unsigned long malloc_start;
+#endif
 #if CONFIG_VAL(SYS_MALLOC_F_LEN)
 	/**
 	 * @malloc_base: base address of early malloc()
@@ -560,6 +566,13 @@
 #define gd_event_state()	NULL
 #endif
 
+#if CONFIG_IS_ENABLED(CMD_BDINFO_EXTRA)
+#define gd_malloc_start()		gd->malloc_start
+#define gd_set_malloc_start(_val)	gd->malloc_start = (_val)
+#else
+#define gd_malloc_start()	0
+#define gd_set_malloc_start(val)
+#endif
 /**
  * enum gd_flags - global data flags
  *
diff --git a/include/bloblist.h b/include/bloblist.h
index 2a2f170..7ea72c6 100644
--- a/include/bloblist.h
+++ b/include/bloblist.h
@@ -113,6 +113,7 @@
 	BLOBLISTT_PROJECT_AREA = 0x8000,
 	BLOBLISTT_U_BOOT_SPL_HANDOFF = 0x8000, /* Hand-off info from SPL */
 	BLOBLISTT_VBE		= 0x8001,	/* VBE per-phase state */
+	BLOBLISTT_U_BOOT_VIDEO = 0x8002, /* Video information from SPL */
 
 	/*
 	 * Vendor-specific tags are permitted here. Projects can be open source
diff --git a/include/bootdev.h b/include/bootdev.h
index e72ef36..1533adf 100644
--- a/include/bootdev.h
+++ b/include/bootdev.h
@@ -200,7 +200,7 @@
  * All fields in @bflow must be set up. Note that @bflow->dev is used to add the
  * bootflow to that device.
  *
- * @dev: Bootdevice device to add to
+ * @dev: Bootdev device to add to
  * @bflow: Bootflow to add. Note that fields within bflow must be allocated
  *	since this function takes over ownership of these. This functions makes
  *	a copy of @bflow itself (without allocating its fields again), so the
diff --git a/include/bootflow.h b/include/bootflow.h
index f20f575..4152577 100644
--- a/include/bootflow.h
+++ b/include/bootflow.h
@@ -58,7 +58,7 @@
  *
  * @bm_node: Points to siblings in the same bootdev
  * @glob_node: Points to siblings in the global list (all bootdev)
- * @dev: Bootdevice device which produced this bootflow
+ * @dev: Bootdev device which produced this bootflow
  * @blk: Block device which contains this bootflow, NULL if this is a network
  *	device or sandbox 'host' device
  * @part: Partition number (0 for whole device)
@@ -81,6 +81,8 @@
  * @fdt_size: Size of FDT file
  * @fdt_addr: Address of loaded fdt
  * @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
  */
 struct bootflow {
 	struct list_head bm_node;
@@ -104,6 +106,8 @@
 	int fdt_size;
 	ulong fdt_addr;
 	int flags;
+	char *cmdline;
+	char *x86_setup;
 };
 
 /**
@@ -440,4 +444,98 @@
 int bootflow_menu_run(struct bootstd_priv *std, bool text_mode,
 		      struct bootflow **bflowp);
 
+#define BOOTFLOWCL_EMPTY	((void *)1)
+
+/**
+ * cmdline_set_arg() - Update or read an argument in a cmdline string
+ *
+ * Handles updating a single arg in a cmdline string, returning it in a supplied
+ * buffer; also reading an arg from a cmdline string
+ *
+ * When updating, consecutive spaces are squashed as are spaces at the start and
+ * end.
+ *
+ * @buf: Working buffer to use (initial contents are ignored). Use NULL when
+ * reading
+ * @maxlen: Length of working buffer. Use 0 when reading
+ * @cmdline: Command line to update, in the form:
+ *
+ *	fred mary= jane=123 john="has spaces"
+ *
+ * @set_arg: Argument to set or read (may or may not exist)
+ * @new_val: Value for the new argument. May not include quotes (") but may
+ * include embedded spaces, in which case it will be quoted when added to the
+ * command line. Use NULL to delete the argument from @cmdline, BOOTFLOWCL_EMPTY
+ * to set it to an empty value (no '=' sign after arg), "" to add an '=' sign
+ * but with an empty value. Use NULL when reading.
+ * @posp: Ignored when setting an argument; when getting an argument, returns
+ * the start position of its value in @cmdline, after the first quote, if any
+ *
+ * Return:
+ * For updating:
+ *	length of new buffer (including \0 terminator) on success, -ENOENT if
+ *	@new_val is NULL and @set_arg does not exist in @from, -EINVAL if a
+ *	quoted arg-value in @from is not terminated with a quote, -EBADF if
+ *	@new_val has spaces but does not start and end with quotes (or it has
+ *	quotes in the middle of the string), -E2BIG if @maxlen is too small
+ * For reading:
+ *	length of arg value (excluding quotes), -ENOENT if not found
+ */
+int cmdline_set_arg(char *buf, int maxlen, const char *cmdline,
+		    const char *set_arg, const char *new_val, int *posp);
+
+/**
+ * bootflow_cmdline_set_arg() - Set a single argument for a bootflow
+ *
+ * Update the allocated cmdline and set the bootargs variable
+ *
+ * @bflow: Bootflow to update
+ * @arg: Argument to update (e.g. "console")
+ * @val: Value to set (e.g. "ttyS2") or NULL to delete the argument if present,
+ * "" to set it to an empty value (e.g. "console=") and BOOTFLOWCL_EMPTY to add
+ * it without any value ("initrd")
+ * @set_env: true to set the "bootargs" environment variable too
+ *
+ * Return: 0 if OK, -ENOMEM if out of memory
+ */
+int bootflow_cmdline_set_arg(struct bootflow *bflow, const char *arg,
+			     const char *val, bool set_env);
+
+/**
+ * cmdline_get_arg() - Read an argument from a cmdline
+ *
+ * @cmdline: Command line to read, in the form:
+ *
+ *	fred mary= jane=123 john="has spaces"
+ * @arg: Argument to read (may or may not exist)
+ * @posp: Returns position of argument (after any leading quote) if present
+ * Return: Length of argument value excluding quotes if found, -ENOENT if not
+ * found
+ */
+int cmdline_get_arg(const char *cmdline, const char *arg, int *posp);
+
+/**
+ * bootflow_cmdline_get_arg() - Read an argument from a cmdline
+ *
+ * @bootflow: Bootflow to read from
+ * @arg: Argument to read (may or may not exist)
+ * @valp: Returns a pointer to the argument (after any leading quote) if present
+ * Return: Length of argument value excluding quotes if found, -ENOENT if not
+ * found
+ */
+int bootflow_cmdline_get_arg(struct bootflow *bflow, const char *arg,
+			     const char **val);
+
+/**
+ * bootflow_cmdline_auto() - Automatically set a value for a known argument
+ *
+ * This handles a small number of known arguments, for Linux in particular. It
+ * adds suitable kernel parameters automatically, e.g. to enable the console.
+ *
+ * @bflow: Bootflow to update
+ * @arg: Name of argument to set (e.g. "earlycon" or "console")
+ * Return: 0 if OK -ve on error
+ */
+int bootflow_cmdline_auto(struct bootflow *bflow, const char *arg);
+
 #endif
diff --git a/include/configs/conga-qeval20-qa3-e3845.h b/include/configs/conga-qeval20-qa3-e3845.h
index 60617e6..03c364f 100644
--- a/include/configs/conga-qeval20-qa3-e3845.h
+++ b/include/configs/conga-qeval20-qa3-e3845.h
@@ -16,8 +16,6 @@
 					"stdout=serial\0" \
 					"stderr=serial\0"
 
-#define VIDEO_IO_OFFSET				0
-
 #undef CFG_EXTRA_ENV_SETTINGS
 #define CFG_EXTRA_ENV_SETTINGS				\
 	"kernel-ver=4.4.0-22\0"					\
diff --git a/include/configs/dfi-bt700.h b/include/configs/dfi-bt700.h
index 05389a43..be095e2 100644
--- a/include/configs/dfi-bt700.h
+++ b/include/configs/dfi-bt700.h
@@ -20,8 +20,6 @@
 					"stdout=serial\0" \
 					"stderr=serial\0"
 
-#define VIDEO_IO_OFFSET				0
-
 #undef CFG_EXTRA_ENV_SETTINGS
 #define CFG_EXTRA_ENV_SETTINGS				\
 	"kernel-ver=4.4.0-24\0"					\
diff --git a/include/configs/minnowmax.h b/include/configs/minnowmax.h
index 4a12c2f..842672d 100644
--- a/include/configs/minnowmax.h
+++ b/include/configs/minnowmax.h
@@ -17,6 +17,4 @@
 					"stderr=vidconsole,serial\0" \
 					"usb_pgood_delay=40\0"
 
-#define VIDEO_IO_OFFSET				0
-
 #endif	/* __CONFIG_H */
diff --git a/include/configs/qemu-x86.h b/include/configs/qemu-x86.h
index 33263a4..3e52352 100644
--- a/include/configs/qemu-x86.h
+++ b/include/configs/qemu-x86.h
@@ -12,14 +12,6 @@
 
 #include <linux/sizes.h>
 
-#define BOOT_TARGET_DEVICES(func) \
-	func(USB, usb, 0) \
-	func(SCSI, scsi, 0) \
-	func(VIRTIO, virtio, 0) \
-	func(IDE, ide, 0) \
-	func(DHCP, dhcp, na)
-
-#include <config_distro_bootcmd.h>
 #include <configs/x86-common.h>
 
 #define CFG_STD_DEVICES_SETTINGS	"stdin=serial,i8042-kbd\0" \
diff --git a/include/configs/som-db5800-som-6867.h b/include/configs/som-db5800-som-6867.h
index b2e7aa1..5f7eabd 100644
--- a/include/configs/som-db5800-som-6867.h
+++ b/include/configs/som-db5800-som-6867.h
@@ -16,6 +16,4 @@
 					"stdout=serial,vidconsole\0" \
 					"stderr=serial,vidconsole\0"
 
-#define VIDEO_IO_OFFSET				0
-
 #endif	/* __CONFIG_H */
diff --git a/include/configs/theadorable-x86-common.h b/include/configs/theadorable-x86-common.h
index b23b878..46aef23 100644
--- a/include/configs/theadorable-x86-common.h
+++ b/include/configs/theadorable-x86-common.h
@@ -15,8 +15,6 @@
 					"stdout=serial\0" \
 					"stderr=serial\0"
 
-#define VIDEO_IO_OFFSET				0
-
 /* Environment settings */
 
 #undef CFG_EXTRA_ENV_SETTINGS
diff --git a/include/configs/x86-chromebook.h b/include/configs/x86-chromebook.h
index 98abb00..6bf90c7 100644
--- a/include/configs/x86-chromebook.h
+++ b/include/configs/x86-chromebook.h
@@ -10,8 +10,6 @@
 #define CFG_X86_REFCODE_ADDR			0xffea0000
 #define CFG_X86_REFCODE_RUN_ADDR		0
 
-#define VIDEO_IO_OFFSET				0
-
 #define CFG_STD_DEVICES_SETTINGS	"stdin=usbkbd,i8042-kbd,serial\0" \
 					"stdout=vidconsole,serial\0" \
 					"stderr=vidconsole,serial\0"
diff --git a/include/env_callback.h b/include/env_callback.h
index a9a14f2..23bc650 100644
--- a/include/env_callback.h
+++ b/include/env_callback.h
@@ -60,8 +60,10 @@
 #define NET6_CALLBACKS
 #endif
 
-#ifdef CONFIG_BOOTSTD
-#define BOOTSTD_CALLBACK	"bootmeths:bootmeths,"
+#ifdef CONFIG_BOOTSTD_FULL
+#define BOOTSTD_CALLBACK \
+	"bootmeths:bootmeths," \
+	"bootargs:bootargs,"
 #else
 #define BOOTSTD_CALLBACK
 #endif
diff --git a/include/part.h b/include/part.h
index be75c73..3b1b539 100644
--- a/include/part.h
+++ b/include/part.h
@@ -598,6 +598,15 @@
 	return ll_entry_start(struct part_driver, part_driver);
 }
 
+/**
+ * part_get_type_by_name() - Get partition type by name
+ *
+ * @name: Name of partition type to look up (not case-sensitive)
+ * Returns: Corresponding partition type (PART_TYPE_...) or PART_TYPE_UNKNOWN if
+ * not known
+ */
+int part_get_type_by_name(const char *name);
+
 #else
 static inline int part_driver_get_count(void)
 { return 0; }
diff --git a/include/video.h b/include/video.h
index e98d0f9..9729fa3 100644
--- a/include/video.h
+++ b/include/video.h
@@ -134,6 +134,30 @@
 
 #define video_get_ops(dev)        ((struct video_ops *)(dev)->driver->ops)
 
+/**
+ * struct video_handoff - video information passed from SPL
+ *
+ * This is used when video is set up by SPL, to provide the details to U-Boot
+ * proper.
+ *
+ * @fb: Base address of frame buffer, 0 if not yet known
+ * @size: Frame-buffer size, in bytes
+ * @xsize:	Number of pixel columns (e.g. 1366)
+ * @ysize:	Number of pixels rows (e.g.. 768)
+ * @line_length:	Length of each frame buffer line, in bytes. This can be
+ *		set by the driver, but if not, the uclass will set it after
+ *		probing
+ * @bpix:	Encoded bits per pixel (enum video_log2_bpp)
+ */
+struct video_handoff {
+	u64 fb;
+	u32 size;
+	u16 xsize;
+	u16 ysize;
+	u32 line_length;
+	u8 bpix;
+};
+
 /** enum colour_idx - the 16 colors supported by consoles */
 enum colour_idx {
 	VID_BLACK = 0,
diff --git a/lib/efi_loader/efi_acpi.c b/lib/efi_loader/efi_acpi.c
index 2ddc350..f755af7 100644
--- a/lib/efi_loader/efi_acpi.c
+++ b/lib/efi_loader/efi_acpi.c
@@ -10,6 +10,9 @@
 #include <log.h>
 #include <mapmem.h>
 #include <acpi/acpi_table.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
 
 static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID;
 
@@ -20,26 +23,28 @@
  */
 efi_status_t efi_acpi_register(void)
 {
-	/* Map within the low 32 bits, to allow for 32bit ACPI tables */
-	u64 acpi = U32_MAX;
+	ulong addr, start, end;
 	efi_status_t ret;
-	ulong addr;
 
-	/* Reserve 64kiB page for ACPI */
-	ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
-				 EFI_ACPI_RECLAIM_MEMORY, 16, &acpi);
+	/* Mark space used for tables */
+	start = ALIGN_DOWN(gd->arch.table_start, EFI_PAGE_MASK);
+	end = ALIGN(gd->arch.table_end, EFI_PAGE_MASK);
+	ret = efi_add_memory_map(start, end - start, EFI_ACPI_RECLAIM_MEMORY);
 	if (ret != EFI_SUCCESS)
 		return ret;
+	if (gd->arch.table_start_high) {
+		start = ALIGN_DOWN(gd->arch.table_start_high, EFI_PAGE_MASK);
+		end = ALIGN(gd->arch.table_end_high, EFI_PAGE_MASK);
+		ret = efi_add_memory_map(start, end - start,
+					 EFI_ACPI_RECLAIM_MEMORY);
+		if (ret != EFI_SUCCESS)
+			return ret;
+	}
 
-	/*
-	 * Generate ACPI tables - we know that efi_allocate_pages() returns
-	 * a 4k-aligned address, so it is safe to assume that
-	 * write_acpi_tables() will write the table at that address.
-	 */
-	addr = map_to_sysmem((void *)(ulong)acpi);
-	write_acpi_tables(addr);
+	addr = gd_acpi_start();
+	printf("EFI using ACPI tables at %lx\n", addr);
 
 	/* And expose them to our EFI payload */
 	return efi_install_configuration_table(&acpi_guid,
-					       (void *)(uintptr_t)acpi);
+					       (void *)(ulong)addr);
 }
diff --git a/lib/uuid.c b/lib/uuid.c
index 96e1af3..ab30fbf 100644
--- a/lib/uuid.c
+++ b/lib/uuid.c
@@ -255,7 +255,7 @@
 		EFI_CERT_TYPE_PKCS7_GUID,
 	},
 #endif
-#ifdef CONFIG_EFI
+#if defined(CONFIG_CMD_EFIDEBUG) || defined(CONFIG_EFI)
 	{ "EFI_LZMA_COMPRESSED", EFI_LZMA_COMPRESSED },
 	{ "EFI_DXE_SERVICES", EFI_DXE_SERVICES },
 	{ "EFI_HOB_LIST", EFI_HOB_LIST },
diff --git a/net/eth_bootdev.c b/net/eth_bootdev.c
index f7b4196..869adf8 100644
--- a/net/eth_bootdev.c
+++ b/net/eth_bootdev.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Bootdevice for ethernet (uses PXE)
+ * Bootdev for ethernet (uses PXE)
  *
  * Copyright 2021 Google LLC
  * Written by Simon Glass <sjg@chromium.org>
diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c
index 2b5f87d..8a4e090 100644
--- a/test/boot/bootflow.c
+++ b/test/boot/bootflow.c
@@ -226,6 +226,7 @@
 	ut_assert_nextlinen("Buffer:    ");
 	ut_assert_nextline("Size:      253 (595 bytes)");
 	ut_assert_nextline("OS:        Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)");
+	ut_assert_nextline("Cmdline:   (none)");
 	ut_assert_nextline("Logo:      (none)");
 	ut_assert_nextline("FDT:       <NULL>");
 	ut_assert_nextline("Error:     0");
@@ -682,3 +683,265 @@
 	return 0;
 }
 BOOTSTD_TEST(bootflow_menu_theme, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/**
+ * check_arg() - Check both the normal case and the buffer-overflow case
+ *
+ * @uts: Unit-test state
+ * @expect_ret: Expected return value (i.e. buffer length)
+ * @expect_str: String expected to be returned
+ * @buf: Buffer to use
+ * @from: Original cmdline to update
+ * @arg: Argument to update (e.g. "console")
+ * @val: Value to set (e.g. "ttyS2") or NULL to delete the argument if present,
+ * "" to set it to an empty value (e.g. "console=") and BOOTFLOWCL_EMPTY to add
+ * it without any value ("initrd")
+ */
+static int check_arg(struct unit_test_state *uts, int expect_ret,
+		     const char *expect_str, char *buf, const char *from,
+		     const char *arg, const char *val)
+{
+	/* check for writing outside the reported bounds */
+	buf[expect_ret] = '[';
+	ut_asserteq(expect_ret,
+		    cmdline_set_arg(buf, expect_ret, from, arg, val, NULL));
+	ut_asserteq_str(expect_str, buf);
+	ut_asserteq('[', buf[expect_ret]);
+
+	/* do the test again but with one less byte in the buffer */
+	ut_asserteq(-E2BIG, cmdline_set_arg(buf, expect_ret - 1, from, arg,
+					    val, NULL));
+
+	return 0;
+}
+
+/* Test of bootflow_cmdline_set_arg() */
+static int test_bootflow_cmdline_set(struct unit_test_state *uts)
+{
+	char buf[50];
+	const int size = sizeof(buf);
+
+	/*
+	 * note that buffer-overflow tests are immediately each test case, just
+	 * top keep the code together
+	 */
+
+	/* add an arg that doesn't already exist, starting from empty */
+	ut_asserteq(-ENOENT, cmdline_set_arg(buf, size, NULL, "me", NULL,
+					     NULL));
+
+	ut_assertok(check_arg(uts, 3, "me", buf, NULL, "me", BOOTFLOWCL_EMPTY));
+	ut_assertok(check_arg(uts, 4, "me=", buf, NULL, "me", ""));
+	ut_assertok(check_arg(uts, 8, "me=fred", buf, NULL, "me", "fred"));
+
+	/* add an arg that doesn't already exist, starting from non-empty */
+	ut_assertok(check_arg(uts, 11, "arg=123 me", buf, "arg=123", "me",
+			      BOOTFLOWCL_EMPTY));
+	ut_assertok(check_arg(uts, 12, "arg=123 me=", buf, "arg=123", "me",
+			      ""));
+	ut_assertok(check_arg(uts, 16, "arg=123 me=fred", buf, "arg=123", "me",
+			      "fred"));
+
+	/* update an arg at the start */
+	ut_assertok(check_arg(uts, 1, "", buf, "arg=123", "arg", NULL));
+	ut_assertok(check_arg(uts, 4, "arg", buf, "arg=123", "arg",
+			      BOOTFLOWCL_EMPTY));
+	ut_assertok(check_arg(uts, 5, "arg=", buf, "arg=123", "arg", ""));
+	ut_assertok(check_arg(uts, 6, "arg=1", buf, "arg=123", "arg", "1"));
+	ut_assertok(check_arg(uts, 9, "arg=1234", buf, "arg=123", "arg",
+			      "1234"));
+
+	/* update an arg at the end */
+	ut_assertok(check_arg(uts, 5, "mary", buf, "mary arg=123", "arg",
+			      NULL));
+	ut_assertok(check_arg(uts, 9, "mary arg", buf, "mary arg=123", "arg",
+			      BOOTFLOWCL_EMPTY));
+	ut_assertok(check_arg(uts, 10, "mary arg=", buf, "mary arg=123", "arg",
+			      ""));
+	ut_assertok(check_arg(uts, 11, "mary arg=1", buf, "mary arg=123", "arg",
+			      "1"));
+	ut_assertok(check_arg(uts, 14, "mary arg=1234", buf, "mary arg=123",
+			      "arg", "1234"));
+
+	/* update an arg in the middle */
+	ut_assertok(check_arg(uts, 16, "mary=abc john=2", buf,
+			      "mary=abc arg=123 john=2", "arg", NULL));
+	ut_assertok(check_arg(uts, 20, "mary=abc arg john=2", buf,
+			      "mary=abc arg=123 john=2", "arg",
+			      BOOTFLOWCL_EMPTY));
+	ut_assertok(check_arg(uts, 21, "mary=abc arg= john=2", buf,
+			      "mary=abc arg=123 john=2", "arg", ""));
+	ut_assertok(check_arg(uts, 22, "mary=abc arg=1 john=2", buf,
+			      "mary=abc arg=123 john=2", "arg", "1"));
+	ut_assertok(check_arg(uts, 25, "mary=abc arg=1234 john=2", buf,
+			      "mary=abc arg=123 john=2", "arg", "1234"));
+
+	/* handle existing args with quotes */
+	ut_assertok(check_arg(uts, 16, "mary=\"abc\" john", buf,
+			      "mary=\"abc\" arg=123 john", "arg", NULL));
+
+	/* handle existing args with quoted spaces */
+	ut_assertok(check_arg(uts, 20, "mary=\"abc def\" john", buf,
+			      "mary=\"abc def\" arg=123 john", "arg", NULL));
+
+	ut_assertok(check_arg(uts, 34, "mary=\"abc def\" arg=123 john def=4",
+			      buf, "mary=\"abc def\" arg=123 john", "def",
+			      "4"));
+
+	/* quote at the start */
+	ut_asserteq(-EBADF, cmdline_set_arg(buf, size,
+					    "mary=\"abc def\" arg=\"123 456\"",
+					    "arg", "\"4 5 6", NULL));
+
+	/* quote at the end */
+	ut_asserteq(-EBADF, cmdline_set_arg(buf, size,
+					    "mary=\"abc def\" arg=\"123 456\"",
+					    "arg", "4 5 6\"", NULL));
+
+	/* quote in the middle */
+	ut_asserteq(-EBADF, cmdline_set_arg(buf, size,
+					    "mary=\"abc def\" arg=\"123 456\"",
+					    "arg", "\"4 \"5 6\"", NULL));
+
+	/* handle updating a quoted arg */
+	ut_assertok(check_arg(uts, 27, "mary=\"abc def\" arg=\"4 5 6\"", buf,
+			      "mary=\"abc def\" arg=\"123 456\"", "arg",
+			      "4 5 6"));
+
+	/* changing a quoted arg to a non-quoted arg */
+	ut_assertok(check_arg(uts, 23, "mary=\"abc def\" arg=789", buf,
+			      "mary=\"abc def\" arg=\"123 456\"", "arg",
+			      "789"));
+
+	/* changing a non-quoted arg to a quoted arg */
+	ut_assertok(check_arg(uts, 29, "mary=\"abc def\" arg=\"456 789\"", buf,
+			      "mary=\"abc def\" arg=123", "arg", "456 789"));
+
+	/* handling of spaces */
+	ut_assertok(check_arg(uts, 8, "arg=123", buf, " ", "arg", "123"));
+	ut_assertok(check_arg(uts, 8, "arg=123", buf, "   ", "arg", "123"));
+	ut_assertok(check_arg(uts, 13, "john arg=123", buf, " john  ", "arg",
+			      "123"));
+	ut_assertok(check_arg(uts, 13, "john arg=123", buf, " john  arg=123  ",
+			      "arg", "123"));
+	ut_assertok(check_arg(uts, 18, "john arg=123 mary", buf,
+			      " john  arg=123 mary ", "arg", "123"));
+
+	/* unchanged arg */
+	ut_assertok(check_arg(uts, 3, "me", buf, "me", "me", BOOTFLOWCL_EMPTY));
+
+	/* arg which starts with the same name */
+	ut_assertok(check_arg(uts, 28, "mary=abc johnathon=2 john=3", buf,
+			      "mary=abc johnathon=2 john=1", "john", "3"));
+
+	return 0;
+}
+BOOTSTD_TEST(test_bootflow_cmdline_set, 0);
+
+/* Test of bootflow_cmdline_set_arg() */
+static int bootflow_set_arg(struct unit_test_state *uts)
+{
+	struct bootflow s_bflow, *bflow = &s_bflow;
+	ulong mem_start;
+
+	ut_assertok(env_set("bootargs", NULL));
+
+	mem_start = ut_check_delta(0);
+
+	/* Do a simple sanity check. Rely on bootflow_cmdline() for the rest */
+	bflow->cmdline = NULL;
+	ut_assertok(bootflow_cmdline_set_arg(bflow, "fred", "123", false));
+	ut_asserteq_str(bflow->cmdline, "fred=123");
+
+	ut_assertok(bootflow_cmdline_set_arg(bflow, "mary", "and here", false));
+	ut_asserteq_str(bflow->cmdline, "fred=123 mary=\"and here\"");
+
+	ut_assertok(bootflow_cmdline_set_arg(bflow, "mary", NULL, false));
+	ut_asserteq_str(bflow->cmdline, "fred=123");
+	ut_assertok(bootflow_cmdline_set_arg(bflow, "fred", NULL, false));
+	ut_asserteq_ptr(bflow->cmdline, NULL);
+
+	ut_asserteq(0, ut_check_delta(mem_start));
+
+	ut_assertok(bootflow_cmdline_set_arg(bflow, "mary", "here", true));
+	ut_asserteq_str("mary=here", env_get("bootargs"));
+	ut_assertok(env_set("bootargs", NULL));
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_set_arg, 0);
+
+/* Test of bootflow_cmdline_get_arg() */
+static int bootflow_cmdline_get(struct unit_test_state *uts)
+{
+	int pos;
+
+	/* empty string */
+	ut_asserteq(-ENOENT, cmdline_get_arg("", "fred", &pos));
+
+	/* arg with empty value */
+	ut_asserteq(0, cmdline_get_arg("fred= mary", "fred", &pos));
+	ut_asserteq(5, pos);
+
+	/* arg with a value */
+	ut_asserteq(2, cmdline_get_arg("fred=23", "fred", &pos));
+	ut_asserteq(5, pos);
+
+	/* arg with a value */
+	ut_asserteq(3, cmdline_get_arg("mary=1 fred=234", "fred", &pos));
+	ut_asserteq(12, pos);
+
+	/* arg with a value, after quoted arg */
+	ut_asserteq(3, cmdline_get_arg("mary=\"1 2\" fred=234", "fred", &pos));
+	ut_asserteq(16, pos);
+
+	/* arg in the middle */
+	ut_asserteq(0, cmdline_get_arg("mary=\"1 2\" fred john=23", "fred",
+				       &pos));
+	ut_asserteq(15, pos);
+
+	/* quoted arg */
+	ut_asserteq(3, cmdline_get_arg("mary=\"1 2\" fred=\"3 4\" john=23",
+				       "fred", &pos));
+	ut_asserteq(17, pos);
+
+	/* args starting with the same prefix */
+	ut_asserteq(1, cmdline_get_arg("mary=abc johnathon=3 john=1", "john",
+				       &pos));
+	ut_asserteq(26, pos);
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_cmdline_get, 0);
+
+static int bootflow_cmdline(struct unit_test_state *uts)
+{
+	ut_assertok(run_command("bootflow scan mmc", 0));
+	ut_assertok(run_command("bootflow sel 0", 0));
+	console_record_reset_enable();
+
+	ut_asserteq(1, run_command("bootflow cmdline get fred", 0));
+	ut_assert_nextline("Argument not found");
+	ut_assert_console_end();
+
+	ut_asserteq(0, run_command("bootflow cmdline set fred 123", 0));
+	ut_asserteq(0, run_command("bootflow cmdline get fred", 0));
+	ut_assert_nextline("123");
+
+	ut_asserteq(0, run_command("bootflow cmdline set mary abc", 0));
+	ut_asserteq(0, run_command("bootflow cmdline get mary", 0));
+	ut_assert_nextline("abc");
+
+	ut_asserteq(0, run_command("bootflow cmdline delete fred", 0));
+	ut_asserteq(1, run_command("bootflow cmdline get fred", 0));
+	ut_assert_nextline("Argument not found");
+
+	ut_asserteq(0, run_command("bootflow cmdline clear mary", 0));
+	ut_asserteq(0, run_command("bootflow cmdline get mary", 0));
+	ut_assert_nextline_empty();
+
+	ut_assert_console_end();
+
+	return 0;
+}
+BOOTSTD_TEST(bootflow_cmdline, 0);
diff --git a/test/cmd/bdinfo.c b/test/cmd/bdinfo.c
index cddf1a4..8c09281 100644
--- a/test/cmd/bdinfo.c
+++ b/test/cmd/bdinfo.c
@@ -16,6 +16,7 @@
 #include <env.h>
 #include <lmb.h>
 #include <net.h>
+#include <serial.h>
 #include <video.h>
 #include <vsprintf.h>
 #include <asm/cache.h>
@@ -191,6 +192,26 @@
 			ut_assert_nextline("devicetree  = %s", fdtdec_get_srcname());
 	}
 
+	if (IS_ENABLED(CONFIG_DM_SERIAL)) {
+		struct serial_device_info info;
+
+		ut_assertnonnull(gd->cur_serial_dev);
+		ut_assertok(serial_getinfo(gd->cur_serial_dev, &info));
+
+		ut_assertok(test_num_l(uts, "serial addr", info.addr));
+		ut_assertok(test_num_l(uts, " width", info.reg_width));
+		ut_assertok(test_num_l(uts, " shift", info.reg_shift));
+		ut_assertok(test_num_l(uts, " offset", info.reg_offset));
+		ut_assertok(test_num_l(uts, " clock", info.clock));
+	}
+
+	if (IS_ENABLED(CONFIG_CMD_BDINFO_EXTRA)) {
+		ut_assert_nextlinen("stack ptr");
+		ut_assertok(test_num_ll(uts, "ram_top ptr",
+					(unsigned long long)gd->ram_top));
+		ut_assertok(test_num_l(uts, "malloc base", gd_malloc_start()));
+	}
+
 	ut_assertok(ut_check_console_end(uts));
 
 	return 0;
diff --git a/test/dm/acpi.c b/test/dm/acpi.c
index 818f715..77eb524 100644
--- a/test/dm/acpi.c
+++ b/test/dm/acpi.c
@@ -609,3 +609,41 @@
 	return 0;
 }
 DM_TEST(dm_test_acpi_cmd_items, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test 'acpi set' command */
+static int dm_test_acpi_cmd_set(struct unit_test_state *uts)
+{
+	struct acpi_ctx ctx;
+	ulong addr;
+	void *buf;
+
+	gd_set_acpi_start(0);
+
+	console_record_reset();
+	ut_asserteq(0, gd_acpi_start());
+	ut_assertok(run_command("acpi set", 0));
+	ut_assert_nextline("ACPI pointer: 0");
+
+	buf = memalign(16, BUF_SIZE);
+	ut_assertnonnull(buf);
+	addr = map_to_sysmem(buf);
+	ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr));
+
+	ut_assertok(acpi_write_dev_tables(&ctx));
+
+	ut_assertok(run_command("acpi set", 0));
+	ut_assert_nextline("ACPI pointer: %lx", addr);
+
+	ut_assertok(run_command("acpi set 0", 0));
+	ut_assert_nextline("Setting ACPI pointer to 0");
+	ut_asserteq(0, gd_acpi_start());
+
+	ut_assertok(run_commandf("acpi set %lx", addr));
+	ut_assert_nextline("Setting ACPI pointer to %lx", addr);
+	ut_asserteq(addr, gd_acpi_start());
+
+	ut_assert_console_end();
+
+	return 0;
+}
+DM_TEST(dm_test_acpi_cmd_set, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/test-main.c b/test/test-main.c
index 2a3b2ba..778bf0a 100644
--- a/test/test-main.c
+++ b/test/test-main.c
@@ -476,7 +476,8 @@
 	 *   (for sandbox we handle this by copying the tree, but not for other
 	 *    boards)
 	 */
-	if (!(test->flags & UT_TESTF_LIVE_TREE) &&
+	if ((test->flags & UT_TESTF_SCAN_FDT) &&
+	    !(test->flags & UT_TESTF_LIVE_TREE) &&
 	    (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) ||
 	     !(test->flags & UT_TESTF_OTHER_FDT)) &&
 	    (!runs || ut_test_run_on_flattree(test)) &&