Merge tag 'dm-pull-28mar21' of git://git.denx.de/u-boot-dm into next

binman support for expanding entries, connections
misc fixes and improvements to sandbox, etc.
x86 CBFS improvements
x86 coreboot improvements
diff --git a/arch/riscv/cpu/ax25/Kconfig b/arch/riscv/cpu/ax25/Kconfig
index 327b74e..941d963 100644
--- a/arch/riscv/cpu/ax25/Kconfig
+++ b/arch/riscv/cpu/ax25/Kconfig
@@ -6,7 +6,7 @@
 	imply RISCV_TIMER if (RISCV_SMODE || SPL_RISCV_SMODE)
 	imply ANDES_PLIC if (RISCV_MMODE || SPL_RISCV_MMODE)
 	imply ANDES_PLMT_TIMER if (RISCV_MMODE || SPL_RISCV_MMODE)
-	imply SPL_CPU_SUPPORT
+	imply SPL_CPU
 	imply SPL_OPENSBI
 	imply SPL_LOAD_FIT
 	help
diff --git a/arch/riscv/cpu/fu540/Kconfig b/arch/riscv/cpu/fu540/Kconfig
index 61bd5c4..616b256 100644
--- a/arch/riscv/cpu/fu540/Kconfig
+++ b/arch/riscv/cpu/fu540/Kconfig
@@ -13,7 +13,7 @@
 	imply RISCV_TIMER if (RISCV_SMODE || SPL_RISCV_SMODE)
 	imply SIFIVE_CLINT if (RISCV_MMODE || SPL_RISCV_MMODE)
 	imply CMD_CPU
-	imply SPL_CPU_SUPPORT
+	imply SPL_CPU
 	imply SPL_OPENSBI
 	imply SPL_LOAD_FIT
 	imply SMP
diff --git a/arch/riscv/cpu/generic/Kconfig b/arch/riscv/cpu/generic/Kconfig
index f4c2e26..198e36e 100644
--- a/arch/riscv/cpu/generic/Kconfig
+++ b/arch/riscv/cpu/generic/Kconfig
@@ -10,6 +10,6 @@
 	imply RISCV_TIMER if (RISCV_SMODE || SPL_RISCV_SMODE)
 	imply SIFIVE_CLINT if (RISCV_MMODE || SPL_RISCV_MMODE)
 	imply CMD_CPU
-	imply SPL_CPU_SUPPORT
+	imply SPL_CPU
 	imply SPL_OPENSBI
 	imply SPL_LOAD_FIT
diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c
index edd48e2..48636ab 100644
--- a/arch/sandbox/cpu/cpu.c
+++ b/arch/sandbox/cpu/cpu.c
@@ -6,7 +6,6 @@
 #include <common.h>
 #include <bootstage.h>
 #include <cpu_func.h>
-#include <dm.h>
 #include <errno.h>
 #include <log.h>
 #include <asm/global_data.h>
@@ -17,7 +16,6 @@
 #include <asm/malloc.h>
 #include <asm/setjmp.h>
 #include <asm/state.h>
-#include <dm/root.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -34,10 +32,8 @@
 {
 	/* Do this here while it still has an effect */
 	os_fd_restore();
-	if (state_uninit())
-		os_exit(2);
 
-	if (dm_uninit())
+	if (state_uninit())
 		os_exit(2);
 
 	/* This is considered normal termination for now */
diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c
index 68825d2..b9ad341 100644
--- a/arch/sandbox/cpu/os.c
+++ b/arch/sandbox/cpu/os.c
@@ -711,7 +711,7 @@
  * @fname: Filename to exec
  * @return does not return on success, any return value is an error
  */
-static int os_jump_to_file(const char *fname)
+static int os_jump_to_file(const char *fname, bool delete_it)
 {
 	struct sandbox_state *state = state_get_current();
 	char mem_fname[30];
@@ -734,11 +734,13 @@
 
 	os_fd_restore();
 
-	extra_args[0] = "-j";
-	extra_args[1] = (char *)fname;
-	extra_args[2] = "-m";
-	extra_args[3] = mem_fname;
-	argc = 4;
+	argc = 0;
+	if (delete_it) {
+		extra_args[argc++] = "-j";
+		extra_args[argc++] = (char *)fname;
+	}
+	extra_args[argc++] = "-m";
+	extra_args[argc++] = mem_fname;
 	if (state->ram_buf_rm)
 		extra_args[argc++] = "--rm_memory";
 	err = add_args(&argv, extra_args, argc);
@@ -762,7 +764,10 @@
 		return err;
 	}
 
-	return unlink(fname);
+	if (delete_it)
+		return unlink(fname);
+
+	return -EFAULT;
 }
 
 int os_jump_to_image(const void *dest, int size)
@@ -774,7 +779,7 @@
 	if (err)
 		return err;
 
-	return os_jump_to_file(fname);
+	return os_jump_to_file(fname, true);
 }
 
 int os_find_u_boot(char *fname, int maxlen, bool use_img)
@@ -847,7 +852,8 @@
 
 	/* U-Boot will delete ram buffer after read: "--rm_memory"*/
 	state->ram_buf_rm = true;
-	return os_jump_to_file(fname);
+
+	return os_jump_to_file(fname, false);
 }
 
 long os_get_time_offset(void)
diff --git a/arch/sandbox/cpu/u-boot.lds b/arch/sandbox/cpu/u-boot.lds
index 936da5e..a1f509c 100644
--- a/arch/sandbox/cpu/u-boot.lds
+++ b/arch/sandbox/cpu/u-boot.lds
@@ -44,6 +44,13 @@
 	{
 		*(.__efi_runtime_rel_stop)
 	}
+
+	.dynsym :
+	{
+		__dyn_sym_start = .;
+		*(.dynsym)
+		__dyn_sym_end = .;
+	}
 }
 
 INSERT BEFORE .data;
diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi
index 69d7d30..31db50d 100644
--- a/arch/sandbox/dts/sandbox.dtsi
+++ b/arch/sandbox/dts/sandbox.dtsi
@@ -200,6 +200,10 @@
 		compatible = "sandbox,reset";
 	};
 
+	rng {
+		compatible = "sandbox,sandbox-rng";
+	};
+
 	sound {
 		compatible = "sandbox,sound";
 		cpu {
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 5b089af..970bdff 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1047,4 +1047,25 @@
 
 endif # INTEL_SOC
 
+config COREBOOT_SYSINFO
+	bool "Support reading coreboot sysinfo"
+	default y if SYS_COREBOOT
+	help
+	  Select this option to read the coreboot sysinfo table on start-up,
+	  if present. This is written by coreboot before it exits and provides
+	  various pieces of information about the running system, including
+	  display, memory and build information. It is stored in
+	  struct sysinfo_t after parsing by get_coreboot_info().
+
+config SPL_COREBOOT_SYSINFO
+	bool "Support reading coreboot sysinfo"
+	depends on SPL
+	default y if COREBOOT_SYSINFO
+	help
+	  Select this option to read the coreboot sysinfo table in SPL,
+	  if present. This is written by coreboot before it exits and provides
+	  various pieces of information about the running system, including
+	  display, memory and build information. It is stored in
+	  struct sysinfo_t after parsing by get_coreboot_info().
+
 endmenu
diff --git a/arch/x86/cpu/apollolake/cpu.c b/arch/x86/cpu/apollolake/cpu.c
index fbc016d..647c9df 100644
--- a/arch/x86/cpu/apollolake/cpu.c
+++ b/arch/x86/cpu/apollolake/cpu.c
@@ -19,6 +19,7 @@
 #include <asm/arch/iomap.h>
 #include <dm/acpi.h>
 
+#ifdef CONFIG_ACPIGEN
 #define CSTATE_RES(address_space, width, offset, address)		\
 	{								\
 	.space_id = address_space,					\
@@ -57,11 +58,6 @@
 	},
 };
 
-static int apl_get_info(const struct udevice *dev, struct cpu_info *info)
-{
-	return cpu_intel_get_info(info, INTEL_BCLK_MHZ);
-}
-
 static int acpi_cpu_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx)
 {
 	uint core_id = dev_seq(dev);
@@ -89,6 +85,12 @@
 
 	return 0;
 }
+#endif /* CONFIG_ACPIGEN */
+
+static int apl_get_info(const struct udevice *dev, struct cpu_info *info)
+{
+	return cpu_intel_get_info(info, INTEL_BCLK_MHZ);
+}
 
 static void update_fixed_mtrrs(void)
 {
@@ -170,9 +172,11 @@
 	return 0;
 }
 
+#ifdef CONFIG_ACPIGEN
 struct acpi_ops apl_cpu_acpi_ops = {
 	.fill_ssdt	= acpi_cpu_fill_ssdt,
 };
+#endif
 
 static const struct cpu_ops cpu_x86_apl_ops = {
 	.get_desc	= cpu_x86_get_desc,
diff --git a/arch/x86/cpu/apollolake/cpu_common.c b/arch/x86/cpu/apollolake/cpu_common.c
index 63f6999..5d7d26b 100644
--- a/arch/x86/cpu/apollolake/cpu_common.c
+++ b/arch/x86/cpu/apollolake/cpu_common.c
@@ -7,11 +7,17 @@
 #include <dm.h>
 #include <log.h>
 #include <asm/cpu_common.h>
+#include <asm/io.h>
 #include <asm/msr.h>
+#include <asm/pci.h>
 #include <asm/arch/cpu.h>
 #include <asm/arch/iomap.h>
+#include <asm/arch/uart.h>
 #include <power/acpi_pmc.h>
 
+/* Define this here to avoid referencing any drivers for the debug UART 1 */
+#define PCH_DEV_P2SB	PCI_BDF(0, 0x0d, 0)
+
 void cpu_flush_l1d_to_l2(void)
 {
 	struct msr_t msr;
@@ -40,3 +46,57 @@
 	debug("PM timer %x %x\n", msr.hi, msr.lo);
 	msr_write(MSR_EMULATE_PM_TIMER, msr);
 }
+
+static void pch_uart_init(void)
+{
+	/*
+	 * Set up the pinmux so that the UART rx/tx signals are connected
+	 * outside the SoC.
+	 *
+	 * There are about 500 lines of code required to program the GPIO
+	 * configuration for the UARTs. But it boils down to four writes, and
+	 * for the debug UART we want the minimum possible amount of code before
+	 * the UART is running. So just add the magic writes here. See
+	 * apl_hostbridge_early_init_pinctrl() for the full horror.
+	 */
+	if (PCI_FUNC(PCH_DEV_UART) == 1) {
+		writel(0x40000402, 0xd0c50650);
+		writel(0x3c47, 0xd0c50654);
+		writel(0x40000400, 0xd0c50658);
+		writel(0x3c48, 0xd0c5065c);
+	} else { /* UART2 */
+		writel(0x40000402, 0xd0c50670);
+		writel(0x3c4b, 0xd0c50674);
+		writel(0x40000400, 0xd0c50678);
+		writel(0x3c4c, 0xd0c5067c);
+	}
+
+#ifdef CONFIG_DEBUG_UART
+	apl_uart_init(PCH_DEV_UART, CONFIG_DEBUG_UART_BASE);
+#endif
+}
+
+static void p2sb_enable_bar(ulong bar)
+{
+	/* Enable PCR Base address in PCH */
+	pci_x86_write_config(PCH_DEV_P2SB, PCI_BASE_ADDRESS_0, bar,
+			     PCI_SIZE_32);
+	pci_x86_write_config(PCH_DEV_P2SB, PCI_BASE_ADDRESS_1, 0, PCI_SIZE_32);
+
+	/* Enable P2SB MSE */
+	pci_x86_write_config(PCH_DEV_P2SB, PCI_COMMAND,
+			     PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY,
+			     PCI_SIZE_8);
+}
+
+/*
+ * board_debug_uart_init() - Init the debug UART ready for use
+ *
+ * This is the minimum init needed to get the UART running. It avoids any
+ * drivers or complex code, so that the UART is running as soon as possible.
+ */
+void board_debug_uart_init(void)
+{
+	p2sb_enable_bar(IOMAP_P2SB_BAR);
+	pch_uart_init();
+}
diff --git a/arch/x86/cpu/apollolake/cpu_spl.c b/arch/x86/cpu/apollolake/cpu_spl.c
index 9a18476..8f48457 100644
--- a/arch/x86/cpu/apollolake/cpu_spl.c
+++ b/arch/x86/cpu/apollolake/cpu_spl.c
@@ -31,68 +31,10 @@
 #include <asm/arch/lpc.h>
 #include <asm/arch/pch.h>
 #include <asm/arch/systemagent.h>
-#include <asm/arch/uart.h>
 #include <asm/fsp2/fsp_api.h>
 #include <linux/sizes.h>
 #include <power/acpi_pmc.h>
 
-/* Define this here to avoid referencing any drivers for the debug UART 1 */
-#define PCH_DEV_P2SB	PCI_BDF(0, 0x0d, 0)
-
-static void pch_uart_init(void)
-{
-	/*
-	 * Set up the pinmux so that the UART rx/tx signals are connected
-	 * outside the SoC.
-	 *
-	 * There are about 500 lines of code required to program the GPIO
-	 * configuration for the UARTs. But it boils down to four writes, and
-	 * for the debug UART we want the minimum possible amount of code before
-	 * the UART is running. So just add the magic writes here. See
-	 * apl_hostbridge_early_init_pinctrl() for the full horror.
-	 */
-	if (PCI_FUNC(PCH_DEV_UART) == 1) {
-		writel(0x40000402, 0xd0c50650);
-		writel(0x3c47, 0xd0c50654);
-		writel(0x40000400, 0xd0c50658);
-		writel(0x3c48, 0xd0c5065c);
-	} else { /* UART2 */
-		writel(0x40000402, 0xd0c50670);
-		writel(0x3c4b, 0xd0c50674);
-		writel(0x40000400, 0xd0c50678);
-		writel(0x3c4c, 0xd0c5067c);
-	}
-
-#ifdef CONFIG_DEBUG_UART
-	apl_uart_init(PCH_DEV_UART, CONFIG_DEBUG_UART_BASE);
-#endif
-}
-
-static void p2sb_enable_bar(ulong bar)
-{
-	/* Enable PCR Base address in PCH */
-	pci_x86_write_config(PCH_DEV_P2SB, PCI_BASE_ADDRESS_0, bar,
-			     PCI_SIZE_32);
-	pci_x86_write_config(PCH_DEV_P2SB, PCI_BASE_ADDRESS_1, 0, PCI_SIZE_32);
-
-	/* Enable P2SB MSE */
-	pci_x86_write_config(PCH_DEV_P2SB, PCI_COMMAND,
-			     PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY,
-			     PCI_SIZE_8);
-}
-
-/*
- * board_debug_uart_init() - Init the debug UART ready for use
- *
- * This is the minimum init needed to get the UART running. It avoids any
- * drivers or complex code, so that the UART is running as soon as possible.
- */
-void board_debug_uart_init(void)
-{
-	p2sb_enable_bar(IOMAP_P2SB_BAR);
-	pch_uart_init();
-}
-
 static int fast_spi_cache_bios_region(void)
 {
 	uint map_size, offset;
diff --git a/arch/x86/cpu/coreboot/Makefile b/arch/x86/cpu/coreboot/Makefile
index 605f903..a6cdb9a 100644
--- a/arch/x86/cpu/coreboot/Makefile
+++ b/arch/x86/cpu/coreboot/Makefile
@@ -20,5 +20,4 @@
 obj-y += sdram.o
 endif
 obj-y += coreboot.o
-obj-y += tables.o
 obj-y += timestamp.o
diff --git a/arch/x86/cpu/coreboot/coreboot.c b/arch/x86/cpu/coreboot/coreboot.c
index 15c3ad8..69cf8f4 100644
--- a/arch/x86/cpu/coreboot/coreboot.c
+++ b/arch/x86/cpu/coreboot/coreboot.c
@@ -14,7 +14,7 @@
 #include <asm/io.h>
 #include <asm/msr.h>
 #include <asm/mtrr.h>
-#include <asm/arch/sysinfo.h>
+#include <asm/cb_sysinfo.h>
 #include <asm/arch/timestamp.h>
 
 DECLARE_GLOBAL_DATA_PTR;
diff --git a/arch/x86/cpu/coreboot/sdram.c b/arch/x86/cpu/coreboot/sdram.c
index a2e47d1..4a256ba 100644
--- a/arch/x86/cpu/coreboot/sdram.c
+++ b/arch/x86/cpu/coreboot/sdram.c
@@ -8,7 +8,7 @@
 #include <common.h>
 #include <init.h>
 #include <asm/e820.h>
-#include <asm/arch/sysinfo.h>
+#include <asm/cb_sysinfo.h>
 #include <asm/global_data.h>
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -16,32 +16,7 @@
 unsigned int install_e820_map(unsigned int max_entries,
 			      struct e820_entry *entries)
 {
-	unsigned int num_entries;
-	int i;
-
-	num_entries = min((unsigned int)lib_sysinfo.n_memranges, max_entries);
-	if (num_entries < lib_sysinfo.n_memranges) {
-		printf("Warning: Limiting e820 map to %d entries.\n",
-			num_entries);
-	}
-	for (i = 0; i < num_entries; i++) {
-		struct memrange *memrange = &lib_sysinfo.memrange[i];
-
-		entries[i].addr = memrange->base;
-		entries[i].size = memrange->size;
-
-		/*
-		 * coreboot has some extensions (type 6 & 16) to the E820 types.
-		 * When we detect this, mark it as E820_RESERVED.
-		 */
-		if (memrange->type == CB_MEM_VENDOR_RSVD ||
-		    memrange->type == CB_MEM_TABLE)
-			entries[i].type = E820_RESERVED;
-		else
-			entries[i].type = memrange->type;
-	}
-
-	return num_entries;
+	return cb_install_e820_map(max_entries, entries);
 }
 
 /*
diff --git a/arch/x86/cpu/coreboot/tables.c b/arch/x86/cpu/coreboot/tables.c
deleted file mode 100644
index c52741a..0000000
--- a/arch/x86/cpu/coreboot/tables.c
+++ /dev/null
@@ -1,255 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-/*
- * This file is part of the libpayload project.
- *
- * Copyright (C) 2008 Advanced Micro Devices, Inc.
- * Copyright (C) 2009 coresystems GmbH
- */
-
-#include <common.h>
-#include <net.h>
-#include <asm/arch/sysinfo.h>
-#include <asm/global_data.h>
-
-DECLARE_GLOBAL_DATA_PTR;
-
-/*
- * This needs to be in the .data section so that it's copied over during
- * relocation. By default it's put in the .bss section which is simply filled
- * with zeroes when transitioning from "ROM", which is really RAM, to other
- * RAM.
- */
-struct sysinfo_t lib_sysinfo __attribute__((section(".data")));
-
-/*
- * Some of this is x86 specific, and the rest of it is generic. Right now,
- * since we only support x86, we'll avoid trying to make lots of infrastructure
- * we don't need. If in the future, we want to use coreboot on some other
- * architecture, then take out the generic parsing code and move it elsewhere.
- */
-
-/* === Parsing code === */
-/* This is the generic parsing code. */
-
-static void cb_parse_memory(unsigned char *ptr, struct sysinfo_t *info)
-{
-	struct cb_memory *mem = (struct cb_memory *)ptr;
-	int count = MEM_RANGE_COUNT(mem);
-	int i;
-
-	if (count > SYSINFO_MAX_MEM_RANGES)
-		count = SYSINFO_MAX_MEM_RANGES;
-
-	info->n_memranges = 0;
-
-	for (i = 0; i < count; i++) {
-		struct cb_memory_range *range =
-		    (struct cb_memory_range *)MEM_RANGE_PTR(mem, i);
-
-		info->memrange[info->n_memranges].base =
-		    UNPACK_CB64(range->start);
-
-		info->memrange[info->n_memranges].size =
-		    UNPACK_CB64(range->size);
-
-		info->memrange[info->n_memranges].type = range->type;
-
-		info->n_memranges++;
-	}
-}
-
-static void cb_parse_serial(unsigned char *ptr, struct sysinfo_t *info)
-{
-	struct cb_serial *ser = (struct cb_serial *)ptr;
-	info->serial = ser;
-}
-
-static void cb_parse_vbnv(unsigned char *ptr, struct sysinfo_t *info)
-{
-	struct cb_vbnv *vbnv = (struct cb_vbnv *)ptr;
-
-	info->vbnv_start = vbnv->vbnv_start;
-	info->vbnv_size = vbnv->vbnv_size;
-}
-
-static void cb_parse_cbmem_entry(unsigned char *ptr, struct sysinfo_t *info)
-{
-	struct cb_cbmem_entry *entry = (struct cb_cbmem_entry *)ptr;
-
-	if (entry->id != CBMEM_ID_SMBIOS)
-		return;
-
-	info->smbios_start = entry->address;
-	info->smbios_size = entry->entry_size;
-}
-
-static void cb_parse_gpios(unsigned char *ptr, struct sysinfo_t *info)
-{
-	int i;
-	struct cb_gpios *gpios = (struct cb_gpios *)ptr;
-
-	info->num_gpios = (gpios->count < SYSINFO_MAX_GPIOS) ?
-				(gpios->count) : SYSINFO_MAX_GPIOS;
-
-	for (i = 0; i < info->num_gpios; i++)
-		info->gpios[i] = gpios->gpios[i];
-}
-
-static void cb_parse_vdat(unsigned char *ptr, struct sysinfo_t *info)
-{
-	struct cb_vdat *vdat = (struct cb_vdat *) ptr;
-
-	info->vdat_addr = vdat->vdat_addr;
-	info->vdat_size = vdat->vdat_size;
-}
-
-static void cb_parse_tstamp(unsigned char *ptr, struct sysinfo_t *info)
-{
-	info->tstamp_table = ((struct cb_cbmem_tab *)ptr)->cbmem_tab;
-}
-
-static void cb_parse_cbmem_cons(unsigned char *ptr, struct sysinfo_t *info)
-{
-	info->cbmem_cons = ((struct cb_cbmem_tab *)ptr)->cbmem_tab;
-}
-
-static void cb_parse_framebuffer(unsigned char *ptr, struct sysinfo_t *info)
-{
-	info->framebuffer = (struct cb_framebuffer *)ptr;
-}
-
-static void cb_parse_string(unsigned char *ptr, char **info)
-{
-	*info = (char *)((struct cb_string *)ptr)->string;
-}
-
-__weak void cb_parse_unhandled(u32 tag, unsigned char *ptr)
-{
-}
-
-static int cb_parse_header(void *addr, int len, struct sysinfo_t *info)
-{
-	unsigned char *ptr = addr;
-	struct cb_header *header;
-	int i;
-
-	header = (struct cb_header *)ptr;
-	if (!header->table_bytes)
-		return 0;
-
-	/* Make sure the checksums match. */
-	if (!ip_checksum_ok(header, sizeof(*header)))
-		return -1;
-
-	if (compute_ip_checksum(ptr + sizeof(*header), header->table_bytes) !=
-	    header->table_checksum)
-		return -1;
-
-	/* Now, walk the tables. */
-	ptr += header->header_bytes;
-
-	/* Inintialize some fields to sentinel values. */
-	info->vbnv_start = info->vbnv_size = (uint32_t)(-1);
-
-	for (i = 0; i < header->table_entries; i++) {
-		struct cb_record *rec = (struct cb_record *)ptr;
-
-		/* We only care about a few tags here (maybe more later). */
-		switch (rec->tag) {
-		case CB_TAG_FORWARD:
-			return cb_parse_header(
-				(void *)(unsigned long)
-				((struct cb_forward *)rec)->forward,
-				len, info);
-			continue;
-		case CB_TAG_MEMORY:
-			cb_parse_memory(ptr, info);
-			break;
-		case CB_TAG_SERIAL:
-			cb_parse_serial(ptr, info);
-			break;
-		case CB_TAG_VERSION:
-			cb_parse_string(ptr, &info->version);
-			break;
-		case CB_TAG_EXTRA_VERSION:
-			cb_parse_string(ptr, &info->extra_version);
-			break;
-		case CB_TAG_BUILD:
-			cb_parse_string(ptr, &info->build);
-			break;
-		case CB_TAG_COMPILE_TIME:
-			cb_parse_string(ptr, &info->compile_time);
-			break;
-		case CB_TAG_COMPILE_BY:
-			cb_parse_string(ptr, &info->compile_by);
-			break;
-		case CB_TAG_COMPILE_HOST:
-			cb_parse_string(ptr, &info->compile_host);
-			break;
-		case CB_TAG_COMPILE_DOMAIN:
-			cb_parse_string(ptr, &info->compile_domain);
-			break;
-		case CB_TAG_COMPILER:
-			cb_parse_string(ptr, &info->compiler);
-			break;
-		case CB_TAG_LINKER:
-			cb_parse_string(ptr, &info->linker);
-			break;
-		case CB_TAG_ASSEMBLER:
-			cb_parse_string(ptr, &info->assembler);
-			break;
-		/*
-		 * FIXME we should warn on serial if coreboot set up a
-		 * framebuffer buf the payload does not know about it.
-		 */
-		case CB_TAG_FRAMEBUFFER:
-			cb_parse_framebuffer(ptr, info);
-			break;
-		case CB_TAG_GPIO:
-			cb_parse_gpios(ptr, info);
-			break;
-		case CB_TAG_VDAT:
-			cb_parse_vdat(ptr, info);
-			break;
-		case CB_TAG_TIMESTAMPS:
-			cb_parse_tstamp(ptr, info);
-			break;
-		case CB_TAG_CBMEM_CONSOLE:
-			cb_parse_cbmem_cons(ptr, info);
-			break;
-		case CB_TAG_VBNV:
-			cb_parse_vbnv(ptr, info);
-			break;
-		case CB_TAG_CBMEM_ENTRY:
-			cb_parse_cbmem_entry(ptr, info);
-			break;
-		default:
-			cb_parse_unhandled(rec->tag, ptr);
-			break;
-		}
-
-		ptr += rec->size;
-	}
-
-	return 1;
-}
-
-/* == Architecture specific == */
-/* This is the x86 specific stuff. */
-
-int get_coreboot_info(struct sysinfo_t *info)
-{
-	long addr;
-	int ret;
-
-	addr = locate_coreboot_table();
-	if (addr < 0)
-		return addr;
-	ret = cb_parse_header((void *)addr, 0x1000, info);
-	if (!ret)
-		return -ENOENT;
-	gd->arch.coreboot_table = addr;
-	gd->flags |= GD_FLG_SKIP_LL_INIT;
-
-	return 0;
-}
diff --git a/arch/x86/cpu/coreboot/timestamp.c b/arch/x86/cpu/coreboot/timestamp.c
index 0162597..7f133ce 100644
--- a/arch/x86/cpu/coreboot/timestamp.c
+++ b/arch/x86/cpu/coreboot/timestamp.c
@@ -8,21 +8,9 @@
 #include <common.h>
 #include <bootstage.h>
 #include <asm/arch/timestamp.h>
-#include <asm/arch/sysinfo.h>
+#include <asm/cb_sysinfo.h>
 #include <linux/compiler.h>
 
-struct timestamp_entry {
-	uint32_t	entry_id;
-	uint64_t	entry_stamp;
-} __packed;
-
-struct timestamp_table {
-	uint64_t	base_time;
-	uint32_t	max_entries;
-	uint32_t	num_entries;
-	struct timestamp_entry entries[0]; /* Variable number of entries */
-} __packed;
-
 static struct timestamp_table *ts_table  __attribute__((section(".data")));
 
 void timestamp_init(void)
diff --git a/arch/x86/cpu/start_from_spl.S b/arch/x86/cpu/start_from_spl.S
index 905c825..abfd4ab 100644
--- a/arch/x86/cpu/start_from_spl.S
+++ b/arch/x86/cpu/start_from_spl.S
@@ -43,6 +43,10 @@
 
 	call	board_init_f_init_reserve
 
+#ifdef CONFIG_DEBUG_UART
+	call	debug_uart_init
+#endif
+
 	call	x86_cpu_reinit_f
 	xorl	%eax, %eax
 	call	board_init_f
diff --git a/arch/x86/dts/chromebook_coral.dts b/arch/x86/dts/chromebook_coral.dts
index d109a38..c8cb4e2 100644
--- a/arch/x86/dts/chromebook_coral.dts
+++ b/arch/x86/dts/chromebook_coral.dts
@@ -47,6 +47,7 @@
 		i2c5 = &i2c_5;
 		i2c6 = &i2c_6;
 		i2c7 = &i2c_7;
+		mmc0 = &emmc;
 		mmc1 = &sdmmc;
 	};
 
@@ -55,6 +56,17 @@
 		recovery-gpios = <&gpio_nw (-1) GPIO_ACTIVE_LOW>;
 		write-protect-gpios = <&gpio_nw GPIO_75 GPIO_ACTIVE_HIGH>;
 		phase-enforce-gpios = <&gpio_n GPIO_10 GPIO_ACTIVE_HIGH>;
+		memconfig-gpios = <&gpio_nw GPIO_101 GPIO_ACTIVE_HIGH
+			&gpio_nw GPIO_102 GPIO_ACTIVE_HIGH
+			&gpio_n GPIO_38 GPIO_ACTIVE_HIGH
+			&gpio_n GPIO_45 GPIO_ACTIVE_HIGH>;
+
+		/*
+		 * This is used for reef only:
+		 *
+		 * skuconfig-gpios = <&gpio_nw GPIO_16 GPIO_ACTIVE_HIGH
+		 *	&gpio_nw GPIO_17 GPIO_ACTIVE_HIGH>;
+		 */
 		smbios {
 			/* Type 1 table */
 			system {
@@ -148,6 +160,11 @@
 		#interrupt-cells = <2>;
 	};
 
+	coreboot-video {
+		/* This will only activate when booted from coreboot */
+		compatible = "coreboot-fb";
+	};
+
 	keyboard {
 		intel,duplicate-por;
 	};
@@ -571,7 +588,7 @@
 		sdmmc: sdmmc@1b,0 {
 			reg = <0x0000d800 0 0 0 0>;
 			compatible = "intel,apl-sd";
-			cd-gpios = <&gpio_n GPIO_177 GPIO_ACTIVE_LOW>;
+			cd-gpios = <&gpio_sw GPIO_177 GPIO_ACTIVE_LOW>;
 			acpi,name = "SDCD";
 		};
 
diff --git a/arch/x86/include/asm/arch-apollolake/uart.h b/arch/x86/include/asm/arch-apollolake/uart.h
index 38335b0..c3ca171 100644
--- a/arch/x86/include/asm/arch-apollolake/uart.h
+++ b/arch/x86/include/asm/arch-apollolake/uart.h
@@ -6,6 +6,7 @@
 #ifndef _ASM_ARCH_UART_H
 #define _ASM_ARCH_UART_H
 
+#include <dt-structs.h>
 #include <ns16550.h>
 
 /**
diff --git a/arch/x86/include/asm/arch-coreboot/sysinfo.h b/arch/x86/include/asm/arch-coreboot/sysinfo.h
deleted file mode 100644
index 419ec52..0000000
--- a/arch/x86/include/asm/arch-coreboot/sysinfo.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* SPDX-License-Identifier: BSD-3-Clause */
-/*
- * This file is part of the libpayload project.
- *
- * Copyright (C) 2008 Advanced Micro Devices, Inc.
- */
-
-#ifndef _COREBOOT_SYSINFO_H
-#define _COREBOOT_SYSINFO_H
-
-#include <asm/coreboot_tables.h>
-
-/* Maximum number of memory range definitions */
-#define SYSINFO_MAX_MEM_RANGES	32
-/* Allow a maximum of 8 GPIOs */
-#define SYSINFO_MAX_GPIOS	8
-
-struct sysinfo_t {
-	int n_memranges;
-	struct memrange {
-		unsigned long long base;
-		unsigned long long size;
-		unsigned int type;
-	} memrange[SYSINFO_MAX_MEM_RANGES];
-
-	u32 cmos_range_start;
-	u32 cmos_range_end;
-	u32 cmos_checksum_location;
-	u32 vbnv_start;
-	u32 vbnv_size;
-
-	char *version;
-	char *extra_version;
-	char *build;
-	char *compile_time;
-	char *compile_by;
-	char *compile_host;
-	char *compile_domain;
-	char *compiler;
-	char *linker;
-	char *assembler;
-
-	struct cb_framebuffer *framebuffer;
-
-	int num_gpios;
-	struct cb_gpio gpios[SYSINFO_MAX_GPIOS];
-
-	void	*vdat_addr;
-	u32	vdat_size;
-	void	*tstamp_table;
-	void	*cbmem_cons;
-	u64 smbios_start;
-	u32 smbios_size;
-
-	struct cb_serial *serial;
-};
-
-extern struct sysinfo_t lib_sysinfo;
-
-int get_coreboot_info(struct sysinfo_t *info);
-
-#endif
diff --git a/arch/x86/include/asm/arch-coreboot/timestamp.h b/arch/x86/include/asm/arch-coreboot/timestamp.h
index 85d42c0..531526b 100644
--- a/arch/x86/include/asm/arch-coreboot/timestamp.h
+++ b/arch/x86/include/asm/arch-coreboot/timestamp.h
@@ -8,30 +8,7 @@
 #ifndef __COREBOOT_TIMESTAMP_H__
 #define __COREBOOT_TIMESTAMP_H__
 
-enum timestamp_id {
-	/* coreboot specific timestamp IDs */
-	TS_START_ROMSTAGE = 1,
-	TS_BEFORE_INITRAM = 2,
-	TS_AFTER_INITRAM = 3,
-	TS_END_ROMSTAGE = 4,
-	TS_START_COPYRAM = 8,
-	TS_END_COPYRAM = 9,
-	TS_START_RAMSTAGE = 10,
-	TS_DEVICE_ENUMERATE = 30,
-	TS_DEVICE_CONFIGURE = 40,
-	TS_DEVICE_ENABLE = 50,
-	TS_DEVICE_INITIALIZE = 60,
-	TS_DEVICE_DONE = 70,
-	TS_CBMEM_POST = 75,
-	TS_WRITE_TABLES = 80,
-	TS_LOAD_PAYLOAD = 90,
-	TS_ACPI_WAKE_JUMP = 98,
-	TS_SELFBOOT_JUMP = 99,
-
-	/* U-Boot entry IDs start at 1000 */
-	TS_U_BOOT_INITTED = 1000, /* This is where u-boot starts */
-	TS_U_BOOT_START_KERNEL = 1100, /* Right before jumping to kernel. */
-};
+#include <asm/cb_sysinfo.h>
 
 void timestamp_init(void);
 void timestamp_add(enum timestamp_id id, uint64_t ts_time);
diff --git a/arch/x86/include/asm/cb_sysinfo.h b/arch/x86/include/asm/cb_sysinfo.h
new file mode 100644
index 0000000..675eef6
--- /dev/null
+++ b/arch/x86/include/asm/cb_sysinfo.h
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2008 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _COREBOOT_SYSINFO_H
+#define _COREBOOT_SYSINFO_H
+
+#include <asm/coreboot_tables.h>
+
+/* Maximum number of memory range definitions */
+#define SYSINFO_MAX_MEM_RANGES	32
+/* Allow a maximum of 8 GPIOs */
+#define SYSINFO_MAX_GPIOS	8
+/* Up to 10 MAC addresses */
+#define SYSINFO_MAX_MACS 10
+
+/**
+ * struct sysinfo_t - Information passed to U-Boot from coreboot
+ *
+ * Coreboot passes on a lot of information using a list of individual data
+ * structures identified by a numeric tag. These are parsed in U-Boot to produce
+ * this struct. Some of the pointers here point back to the tagged data
+ * structure, since it is assumed to remain around while U-Boot is running.
+ *
+ * The 'cbsysinfo' command can display this information.
+ *
+ * @cpu_khz: CPU frequence in KHz (e.g. 1100000)
+ * @serial: Pointer to the serial information, NULL if none
+ * @ser_ioport: Not actually provided by a tag and not used on modern hardware,
+ *	which typicaally uses a memory-mapped port
+ * @ser_base: Not used at all, but present to match up with the coreboot data
+ *	structure
+ * @n_memranges: Number of memory ranges
+ * @memrange: List of memory ranges:
+ *	@base: Base address of range
+ *	@size: Size of range in bytes
+ *	@type: Type of range (CB_MEM_RAM, etc.)
+ * @option_table: Provides a pointer to the CMOS RAM options table, which
+ *	indicates which options are available. The header is followed by a list
+ *	of struct cb_cmos_entries records, so that an option can be found from
+ *	its name. This is not used in U-Boot. NULL if not present
+ * @cmos_range_start: Start bit of the CMOS checksum range (in fact this must
+ *	be a multiple of 8)
+ * @cmos_range_end: End bit of the CMOS checksum range (multiple of 8). This is
+ *	the inclusive end.
+ * @cmos_checksum_location: Location of checksum, multiplied by 8. This is the
+ *	byte offset into the CMOS RAM of the first checksum byte. The second one
+ *	follows immediately. The checksum is a simple 16-bit sum of all the
+ *	bytes from offset cmos_range_start / 8 to cmos_range_end / 8, inclusive,
+ *	in big-endian format (so sum >> 8 is stored in the first byte).
+ * @vbnv_start: Start offset of CMOS RAM used for Chromium OS verified boot
+ *	(typically 0x34)
+ * @vbnv_size: Number of bytes used by Chromium OS verified boot (typically
+ *	0x10)
+ * @extra_version: Extra version information, typically ""
+ * @build: Build date, e.g. "Wed Nov 18 02:51:58 UTC 2020"
+ * @compile_time: Compilation time, e.g. "02:51:58"
+ * @compile_by: Who compiled coreboot (never set?)
+ * @compile_host: Name of the machine that compiled coreboot (never set?)
+ * @compile_domain: Domain name of the machine that compiled coreboot (never
+ *	set?)
+ * @compiler: Name of the compiler used to build coreboot (never set?)
+ * @linker: Name of the linker used to build coreboot (never set?)
+ * @assembler: Name of the assembler used to build coreboot (never set?)
+ * @cb_version: Coreboot version string, e.g. v1.9308_26_0.0.22-2599-g232f22c75d
+ * @framebuffer: Address of framebuffer tag, or NULL if none. See
+ *	struct cb_framebuffer for the definition
+ * @num_gpios: Number of verified-boot GPIOs
+ * @gpios: List of GPIOs:
+ *	@port: GPIO number, or 0xffffffff if not a GPIO
+ *	@polarity: CB_GPIO_ACTIVE_LOW or CB_GPIO_ACTIVE_HIGH
+ *	@value: Value of GPIO (0 or 1)
+ *	@name: Name of GPIO
+ *
+ *	A typical list is:
+ *	  id: port     polarity val name
+ *	   0:    -  active-high   1 write protect
+ *	   1:    -  active-high   0 recovery
+ *	   2:    -  active-high   1 lid
+ *	   3:    -  active-high   0 power
+ *	   4:    -  active-high   0 oprom
+ *	   5:   29  active-high   0 EC in RW
+ *
+ * @num_macs: Number of MAC addresses
+ * @macs: List of MAC addresses
+ * @serialno: Serial number, or NULL (never set?)
+ * @mbtable: Address of the multiboot table, or NULL. This is a
+ *	struct multiboot_header, not used in U-Boot
+ * @header: Address of header, if there is a CB_TAG_FORWARD, else NULL
+ * @mainboard: Pointer to mainboard info or NULL. Typically the vendor is
+ *	"Google" and the part number is ""
+ * @vboot_handoff: Pointer to Chromium OS verified boot hand-off information.
+ *	This is struct vboot_handoff, providing access to internal information
+ *	generated by coreboot when this is being used
+ * @vboot_handoff_size: Size of hand-off information (typically 0xc0c)
+ * @vdat_addr: Pointer to Chromium OS verified boot data, which uses
+ *	struct chromeos_acpi. It sits in the Intel Global NVS struct, after the
+ *	first 0x100 bytes
+ * @vdat_size: Size of this data, typically 0xf00
+ * @smbios_start: Address of SMBIOS tables
+ * @smbios_size: Size of SMBIOS tables (e.g. 0x800)
+ * @x86_rom_var_mtrr_index: MTRR number used for ROM caching. Not used in U-Boot
+ * @tstamp_table: Pointer to timestamp_table, struct timestamp_table
+ * @cbmem_cons: Pointer to the console dump, struct cbmem_console. This provides
+ *	access to the console output generated by coreboot, typically about 64KB
+ *	and mostly PCI enumeration info
+ * @mrc_cache: Pointer to memory-reference-code cache, typically NULL
+ * acpi_gnvs: @Pointer to Intel Global NVS struct, see struct acpi_global_nvs
+ * @board_id: Board ID indicating the board variant, typically 0xffffffff
+ * @ram_code: RAM code indicating the SDRAM type, typically 0xffffffff
+ * @wifi_calibration: WiFi calibration info, NULL if none
+ * @ramoops_buffer: Address of kernel Ramoops buffer
+ * @ramoops_buffer_size: Sizeof of Ramoops buffer, typically 1MB
+ * @spi_flash: Information about SPI flash:
+ *	@size: Size in bytes, e.g. 16MB
+ *	@sector_size; Sector size of flash device, e.g. 4KB
+ *	@erase_cmd: Command used to erase flash, or 0 if not used
+ * @fmap_offset: SPI-flash offset of the flash map (FMAP) table. This has a
+ *	__FMAP__ header. It provides information about the different top-level
+ *	sections in the SPI flash, e.g. 0x204000
+ * @cbfs_offset: SPI-flash offset of the Coreboot Filesystem (CBFS) used for
+ *	read-only data, e.g. 0x205000. This is typically called 'COREBOOT' in
+ *	the flash map. It holds various coreboot binaries as well as
+ *	video-configuration files and graphics data for the Chromium OS
+ *	verified boot user interface.
+ * @cbfs_size: Size of CBFS, e.g. 0x17b000
+ * @boot_media_size; Size of boot media (i.e. SPI flash), e.g. 16MB
+ * @mtc_start; Start of MTC region (Nvidia private data), 0 if not used. See
+ *	https://chromium.googlesource.com/chromiumos/third_party/coreboot/+/chromeos-2013.04/src/soc/nvidia/tegra210/mtc.c
+ * @mtc_size: Size of MTC region
+ * @chromeos_vpd: Chromium OS Vital Product Data region, typically NULL, meaning
+ *	not used
+ */
+struct sysinfo_t {
+	unsigned int cpu_khz;
+	struct cb_serial *serial;
+	unsigned short ser_ioport;
+	unsigned long ser_base; // for mmapped serial
+
+	int n_memranges;
+
+	struct memrange {
+		unsigned long long base;
+		unsigned long long size;
+		unsigned int type;
+	} memrange[SYSINFO_MAX_MEM_RANGES];
+
+	struct cb_cmos_option_table *option_table;
+	u32 cmos_range_start;
+	u32 cmos_range_end;
+	u32 cmos_checksum_location;
+	u32 vbnv_start;
+	u32 vbnv_size;
+
+	char *version;
+	char *extra_version;
+	char *build;
+	char *compile_time;
+	char *compile_by;
+	char *compile_host;
+	char *compile_domain;
+	char *compiler;
+	char *linker;
+	char *assembler;
+
+	char *cb_version;
+
+	struct cb_framebuffer *framebuffer;
+
+	int num_gpios;
+	struct cb_gpio gpios[SYSINFO_MAX_GPIOS];
+	int num_macs;
+	struct mac_address macs[SYSINFO_MAX_MACS];
+	char *serialno;
+
+	unsigned long *mbtable; /** Pointer to the multiboot table */
+
+	struct cb_header *header;
+	struct cb_mainboard *mainboard;
+
+	void	*vboot_handoff;
+	u32	vboot_handoff_size;
+	void	*vdat_addr;
+	u32	vdat_size;
+	u64 smbios_start;
+	u32 smbios_size;
+
+	int x86_rom_var_mtrr_index;
+
+	void		*tstamp_table;
+	void		*cbmem_cons;
+	void		*mrc_cache;
+	void		*acpi_gnvs;
+	u32		board_id;
+	u32		ram_code;
+	void		*wifi_calibration;
+	u64	ramoops_buffer;
+	u32	ramoops_buffer_size;
+	struct {
+		u32 size;
+		u32 sector_size;
+		u32 erase_cmd;
+	} spi_flash;
+	u64 fmap_offset;
+	u64 cbfs_offset;
+	u64 cbfs_size;
+	u64 boot_media_size;
+	u64 mtc_start;
+	u32 mtc_size;
+	void	*chromeos_vpd;
+};
+
+extern struct sysinfo_t lib_sysinfo;
+
+int get_coreboot_info(struct sysinfo_t *info);
+
+#endif
diff --git a/arch/x86/include/asm/coreboot_tables.h b/arch/x86/include/asm/coreboot_tables.h
index 7e15767..a74654b 100644
--- a/arch/x86/include/asm/coreboot_tables.h
+++ b/arch/x86/include/asm/coreboot_tables.h
@@ -8,6 +8,106 @@
 #ifndef _COREBOOT_TABLES_H
 #define _COREBOOT_TABLES_H
 
+struct timestamp_entry {
+	u32	entry_id;
+	u64	entry_stamp;
+} __packed;
+
+struct timestamp_table {
+	u64	base_time;
+	u16	max_entries;
+	u16	tick_freq_mhz;
+	u32	num_entries;
+	struct timestamp_entry entries[0]; /* Variable number of entries */
+} __packed;
+
+enum timestamp_id {
+	/* coreboot-specific timestamp IDs */
+	TS_START_ROMSTAGE = 1,
+	TS_BEFORE_INITRAM = 2,
+	TS_AFTER_INITRAM = 3,
+	TS_END_ROMSTAGE = 4,
+	TS_START_VBOOT = 5,
+	TS_END_VBOOT = 6,
+	TS_START_COPYRAM = 8,
+	TS_END_COPYRAM = 9,
+	TS_START_RAMSTAGE = 10,
+	TS_START_BOOTBLOCK = 11,
+	TS_END_BOOTBLOCK = 12,
+	TS_START_COPYROM = 13,
+	TS_END_COPYROM = 14,
+	TS_START_ULZMA = 15,
+	TS_END_ULZMA = 16,
+	TS_START_ULZ4F = 17,
+	TS_END_ULZ4F = 18,
+	TS_DEVICE_ENUMERATE = 30,
+	TS_DEVICE_CONFIGURE = 40,
+	TS_DEVICE_ENABLE = 50,
+	TS_DEVICE_INITIALIZE = 60,
+	TS_DEVICE_DONE = 70,
+	TS_CBMEM_POST = 75,
+	TS_WRITE_TABLES = 80,
+	TS_FINALIZE_CHIPS = 85,
+	TS_LOAD_PAYLOAD = 90,
+	TS_ACPI_WAKE_JUMP = 98,
+	TS_SELFBOOT_JUMP = 99,
+
+	/* 500+ reserved for vendorcode extensions (500-600: google/chromeos) */
+	TS_START_COPYVER = 501,
+	TS_END_COPYVER = 502,
+	TS_START_TPMINIT = 503,
+	TS_END_TPMINIT = 504,
+	TS_START_VERIFY_SLOT = 505,
+	TS_END_VERIFY_SLOT = 506,
+	TS_START_HASH_BODY = 507,
+	TS_DONE_LOADING = 508,
+	TS_DONE_HASHING = 509,
+	TS_END_HASH_BODY = 510,
+	TS_START_COPYVPD = 550,
+	TS_END_COPYVPD_RO = 551,
+	TS_END_COPYVPD_RW = 552,
+
+	/* 940-950 reserved for vendorcode extensions (940-950: Intel ME) */
+	TS_ME_INFORM_DRAM_WAIT = 940,
+	TS_ME_INFORM_DRAM_DONE = 941,
+
+	/* 950+ reserved for vendorcode extensions (950-999: intel/fsp) */
+	TS_FSP_MEMORY_INIT_START = 950,
+	TS_FSP_MEMORY_INIT_END = 951,
+	TS_FSP_TEMP_RAM_EXIT_START = 952,
+	TS_FSP_TEMP_RAM_EXIT_END = 953,
+	TS_FSP_SILICON_INIT_START = 954,
+	TS_FSP_SILICON_INIT_END = 955,
+	TS_FSP_BEFORE_ENUMERATE = 956,
+	TS_FSP_AFTER_ENUMERATE = 957,
+	TS_FSP_BEFORE_FINALIZE = 958,
+	TS_FSP_AFTER_FINALIZE = 959,
+	TS_FSP_BEFORE_END_OF_FIRMWARE = 960,
+	TS_FSP_AFTER_END_OF_FIRMWARE = 961,
+
+	/* 1000+ reserved for payloads (1000-1200: ChromeOS depthcharge) */
+
+	/* U-Boot entry IDs start at 1000 */
+	TS_U_BOOT_INITTED = 1000, /* This is where U-Boot starts */
+
+	TS_RO_PARAMS_INIT = 1001,
+	TS_RO_VB_INIT = 1002,
+	TS_RO_VB_SELECT_FIRMWARE = 1003,
+	TS_RO_VB_SELECT_AND_LOAD_KERNEL = 1004,
+
+	TS_RW_VB_SELECT_AND_LOAD_KERNEL = 1010,
+
+	TS_VB_SELECT_AND_LOAD_KERNEL = 1020,
+	TS_VB_EC_VBOOT_DONE = 1030,
+	TS_VB_STORAGE_INIT_DONE = 1040,
+	TS_VB_READ_KERNEL_DONE = 1050,
+	TS_VB_VBOOT_DONE = 1100,
+
+	TS_START_KERNEL = 1101,
+	TS_KERNEL_DECOMPRESSION = 1102,
+	TS_U_BOOT_START_KERNEL = 1100, /* Right before jumping to kernel */
+};
+
 struct memory_area;
 
 struct cbuint64 {
@@ -162,13 +262,14 @@
 };
 
 #define CB_TAG_GPIO			0x0013
-#define GPIO_MAX_NAME_LENGTH		16
-
+#define CB_GPIO_ACTIVE_LOW		0
+#define CB_GPIO_ACTIVE_HIGH		1
+#define CB_GPIO_MAX_NAME_LENGTH		16
 struct cb_gpio {
 	u32 port;
 	u32 polarity;
 	u32 value;
-	u8 name[GPIO_MAX_NAME_LENGTH];
+	u8 name[CB_GPIO_MAX_NAME_LENGTH];
 };
 
 struct cb_gpios {
@@ -181,61 +282,158 @@
 #define CB_TAG_FDT			0x0014
 
 struct cb_fdt {
-	uint32_t tag;
-	uint32_t size;	/* size of the entire entry */
+	u32 tag;
+	u32 size;	/* size of the entire entry */
 	/* the actual FDT gets placed here */
 };
 
 #define CB_TAG_VDAT			0x0015
 
 struct cb_vdat {
-	uint32_t tag;
-	uint32_t size;	/* size of the entire entry */
+	u32 tag;
+	u32 size;	/* size of the entire entry */
 	void *vdat_addr;
-	uint32_t vdat_size;
+	u32 vdat_size;
 };
 
 #define CB_TAG_TIMESTAMPS		0x0016
 #define CB_TAG_CBMEM_CONSOLE		0x0017
+
+struct cbmem_console {
+	u32 size;
+	u32 cursor;
+	char body[0];
+} __packed;
+
 #define CB_TAG_MRC_CACHE		0x0018
 
 struct cb_cbmem_tab {
-	uint32_t tag;
-	uint32_t size;
-	void *cbmem_tab;
+	u32 tag;
+	u32 size;
+	u64 cbmem_tab;
 };
 
 #define CB_TAG_VBNV			0x0019
 
 struct cb_vbnv {
-	uint32_t tag;
-	uint32_t size;
-	uint32_t vbnv_start;
-	uint32_t vbnv_size;
+	u32 tag;
+	u32 size;
+	u32 vbnv_start;
+	u32 vbnv_size;
+};
+
+#define CB_TAG_VBOOT_HANDOFF		0x0020
+
+#define CB_TAG_X86_ROM_MTRR		0x0021
+struct cb_x86_rom_mtrr {
+	u32 tag;
+	u32 size;
+	/*
+	 * The variable range MTRR index covering the ROM. If one wants to
+	 * enable caching the ROM, the variable MTRR needs to be set to
+	 * write-protect. To disable the caching after enabling set the
+	 * type to uncacheable
+	 */
+	u32 index;
+};
+
+#define CB_TAG_DMA			0x0022
+#define CB_TAG_RAM_OOPS			0x0023
+#define CB_TAG_ACPI_GNVS		0x0024
+
+#define CB_TAG_BOARD_ID			0x0025
+struct cb_board_id {
+	u32 tag;
+	u32 size;
+	/* Board ID as retrieved from the board revision GPIOs. */
+	u32 board_id;
+};
+
+#define CB_TAG_MAC_ADDRS		0x0026
+struct mac_address {
+	u8 mac_addr[6];
+	u8 pad[2];         /* Pad it to 8 bytes to keep it simple. */
+};
+
+struct cb_macs {
+	u32 tag;
+	u32 size;
+	u32 count;
+	struct mac_address mac_addrs[0];
+};
+
+#define CB_TAG_WIFI_CALIBRATION		0x0027
+
+#define CB_TAG_RAM_CODE			0x0028
+struct cb_ram_code {
+	u32 tag;
+	u32 size;
+	u32 ram_code;
+};
+
+#define CB_TAG_SPI_FLASH		0x0029
+struct cb_spi_flash {
+	u32 tag;
+	u32 size;
+	u32 flash_size;
+	u32 sector_size;
+	u32 erase_cmd;
 };
 
-#define CB_TAG_CBMEM_ENTRY 0x0031
-#define CBMEM_ID_SMBIOS    0x534d4254
+#define CB_TAG_MTC			0x002b
+#define CB_TAG_VPD			0x002c
+struct lb_range {
+	u32 tag;
+	u32 size;
+	u64 range_start;
+	u32 range_size;
+};
+
+#define CB_TAG_BOOT_MEDIA_PARAMS	0x0030
+struct cb_boot_media_params {
+	u32 tag;
+	u32 size;
+	/* offsets are relative to start of boot media */
+	u64 fmap_offset;
+	u64 cbfs_offset;
+	u64 cbfs_size;
+	u64 boot_media_size;
+};
+
+#define CB_TAG_CBMEM_ENTRY		0x0031
+#define CBMEM_ID_SMBIOS			0x534d4254
 
 struct cb_cbmem_entry {
-	uint32_t tag;
-	uint32_t size;
-	uint64_t address;
-	uint32_t entry_size;
-	uint32_t id;
+	u32 tag;
+	u32 size;
+	u64 address;
+	u32 entry_size;
+	u32 id;
+};
+
+#define CB_TAG_TSC_INFO			0x0032
+struct cb_tsc_info {
+	u32 tag;
+	u32 size;
+
+	u32 freq_khz;
 };
 
+#define CB_TAG_SERIALNO			0x002a
+#define CB_MAX_SERIALNO_LENGTH		32
+
 #define CB_TAG_CMOS_OPTION_TABLE	0x00c8
 
 struct cb_cmos_option_table {
 	u32 tag;
 	u32 size;
 	u32 header_length;
+	/* entries follow after this header */
 };
 
 #define CB_TAG_OPTION			0x00c9
 
-#define CMOS_MAX_NAME_LENGTH		32
+#define CB_CMOS_MAX_NAME_LENGTH		32
 
 struct cb_cmos_entries {
 	u32 tag;
@@ -244,34 +442,33 @@
 	u32 length;
 	u32 config;
 	u32 config_id;
-	u8 name[CMOS_MAX_NAME_LENGTH];
+	u8 name[CB_CMOS_MAX_NAME_LENGTH];
 };
 
 #define CB_TAG_OPTION_ENUM		0x00ca
-#define CMOS_MAX_TEXT_LENGTH		32
-
+#define CB_CMOS_MAX_TEXT_LENGTH		32
 struct cb_cmos_enums {
 	u32 tag;
 	u32 size;
 	u32 config_id;
 	u32 value;
-	u8 text[CMOS_MAX_TEXT_LENGTH];
+	u8 text[CB_CMOS_MAX_TEXT_LENGTH];
 };
 
 #define CB_TAG_OPTION_DEFAULTS		0x00cb
-#define CMOS_IMAGE_BUFFER_SIZE		128
+#define CB_CMOS_IMAGE_BUFFER_SIZE	128
 
 struct cb_cmos_defaults {
 	u32 tag;
 	u32 size;
 	u32 name_length;
-	u8 name[CMOS_MAX_NAME_LENGTH];
-	u8 default_set[CMOS_IMAGE_BUFFER_SIZE];
+	u8 name[CB_CMOS_MAX_NAME_LENGTH];
+	u8 default_set[CB_CMOS_IMAGE_BUFFER_SIZE];
 };
 
 #define CB_TAG_OPTION_CHECKSUM		0x00cc
-#define CHECKSUM_NONE			0
-#define CHECKSUM_PCBIOS			1
+#define CB_CHECKSUM_NONE		0
+#define CB_CHECKSUM_PCBIOS		1
 
 struct	cb_cmos_checksum {
 	u32 tag;
diff --git a/arch/x86/include/asm/e820.h b/arch/x86/include/asm/e820.h
index a66c0d2..850a0a7 100644
--- a/arch/x86/include/asm/e820.h
+++ b/arch/x86/include/asm/e820.h
@@ -22,9 +22,23 @@
 #define ISA_START_ADDRESS	0xa0000
 #define ISA_END_ADDRESS		0x100000
 
-/* Implementation defined function to install an e820 map */
+/* Implementation-defined function to install an e820 map */
 unsigned int install_e820_map(unsigned int max_entries,
 			      struct e820_entry *);
+
+/**
+ * cb_install_e820_map() - Install e820 map provided by coreboot sysinfo
+ *
+ * This should be used when booting from coreboot, since in that case the
+ * memory areas are provided by coreboot in its sysinfo.
+ *
+ * @max_entries: Maximum number of entries to write
+ * @entries: Place to put entires
+ * @return number of entries written
+ */
+unsigned int cb_install_e820_map(unsigned int max_entries,
+				 struct e820_entry *entries);
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* _ASM_X86_E820_H */
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 1bcbb49..65d9b3b 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -15,6 +15,7 @@
 obj-$(CONFIG_CMD_BOOTM) += bootm.o
 endif
 obj-y	+= cmd_boot.o
+obj-$(CONFIG_$(SPL_)COREBOOT_SYSINFO)	+= coreboot/
 obj-$(CONFIG_SEABIOS) += coreboot_table.o
 obj-y	+= early_cmos.o
 obj-y	+= e820.o
diff --git a/arch/x86/lib/bootm.c b/arch/x86/lib/bootm.c
index aa5f0bf..733dd71 100644
--- a/arch/x86/lib/bootm.c
+++ b/arch/x86/lib/bootm.c
@@ -36,7 +36,7 @@
 	printf("\nStarting kernel ...\n\n");
 
 #ifdef CONFIG_SYS_COREBOOT
-	timestamp_add_now(TS_U_BOOT_START_KERNEL);
+	timestamp_add_now(TS_START_KERNEL);
 #endif
 	bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
 #if CONFIG_IS_ENABLED(BOOTSTAGE_REPORT)
diff --git a/arch/x86/lib/coreboot/Makefile b/arch/x86/lib/coreboot/Makefile
new file mode 100644
index 0000000..cb0ae1d
--- /dev/null
+++ b/arch/x86/lib/coreboot/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier:	GPL-2.0+
+#
+# Copyright 2021 Google LLC
+#
+
+obj-y += cb_sysinfo.o
+obj-y += cb_support.o
diff --git a/arch/x86/lib/coreboot/cb_support.c b/arch/x86/lib/coreboot/cb_support.c
new file mode 100644
index 0000000..ebb45cd
--- /dev/null
+++ b/arch/x86/lib/coreboot/cb_support.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Support for booting from coreboot
+ *
+ * Copyright 2021 Google LLC
+ */
+
+#include <common.h>
+#include <asm/cb_sysinfo.h>
+#include <asm/e820.h>
+
+unsigned int cb_install_e820_map(unsigned int max_entries,
+				 struct e820_entry *entries)
+{
+	unsigned int num_entries;
+	int i;
+
+	num_entries = min((unsigned int)lib_sysinfo.n_memranges, max_entries);
+	if (num_entries < lib_sysinfo.n_memranges) {
+		printf("Warning: Limiting e820 map to %d entries\n",
+		       num_entries);
+	}
+	for (i = 0; i < num_entries; i++) {
+		struct memrange *memrange = &lib_sysinfo.memrange[i];
+
+		entries[i].addr = memrange->base;
+		entries[i].size = memrange->size;
+
+		/*
+		 * coreboot has some extensions (type 6 & 16) to the E820 types.
+		 * When we detect this, mark it as E820_RESERVED.
+		 */
+		if (memrange->type == CB_MEM_VENDOR_RSVD ||
+		    memrange->type == CB_MEM_TABLE)
+			entries[i].type = E820_RESERVED;
+		else
+			entries[i].type = memrange->type;
+	}
+
+	return num_entries;
+}
diff --git a/arch/x86/lib/coreboot/cb_sysinfo.c b/arch/x86/lib/coreboot/cb_sysinfo.c
new file mode 100644
index 0000000..e2c65bf
--- /dev/null
+++ b/arch/x86/lib/coreboot/cb_sysinfo.c
@@ -0,0 +1,468 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * This file is part of the libpayload project.
+ *
+ * Copyright (C) 2008 Advanced Micro Devices, Inc.
+ * Copyright (C) 2009 coresystems GmbH
+ */
+
+#include <common.h>
+#include <asm/cb_sysinfo.h>
+#include <init.h>
+#include <mapmem.h>
+#include <net.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * This needs to be in the .data section so that it's copied over during
+ * relocation. By default it's put in the .bss section which is simply filled
+ * with zeroes when transitioning from "ROM", which is really RAM, to other
+ * RAM.
+ */
+struct sysinfo_t lib_sysinfo __attribute__((section(".data")));
+
+/*
+ * Some of this is x86 specific, and the rest of it is generic. Right now,
+ * since we only support x86, we'll avoid trying to make lots of infrastructure
+ * we don't need. If in the future, we want to use coreboot on some other
+ * architecture, then take out the generic parsing code and move it elsewhere.
+ */
+
+/* === Parsing code === */
+/* This is the generic parsing code */
+
+static void cb_parse_memory(unsigned char *ptr, struct sysinfo_t *info)
+{
+	struct cb_memory *mem = (struct cb_memory *)ptr;
+	int count = MEM_RANGE_COUNT(mem);
+	int i;
+
+	if (count > SYSINFO_MAX_MEM_RANGES)
+		count = SYSINFO_MAX_MEM_RANGES;
+
+	info->n_memranges = 0;
+
+	for (i = 0; i < count; i++) {
+		struct cb_memory_range *range =
+		    (struct cb_memory_range *)MEM_RANGE_PTR(mem, i);
+
+		info->memrange[info->n_memranges].base =
+		    UNPACK_CB64(range->start);
+
+		info->memrange[info->n_memranges].size =
+		    UNPACK_CB64(range->size);
+
+		info->memrange[info->n_memranges].type = range->type;
+
+		info->n_memranges++;
+	}
+}
+
+static void cb_parse_serial(unsigned char *ptr, struct sysinfo_t *info)
+{
+	struct cb_serial *ser = (struct cb_serial *)ptr;
+
+	info->serial = ser;
+}
+
+static void cb_parse_vboot_handoff(unsigned char *ptr, struct sysinfo_t *info)
+{
+	struct lb_range *vbho = (struct lb_range *)ptr;
+
+	info->vboot_handoff = (void *)(uintptr_t)vbho->range_start;
+	info->vboot_handoff_size = vbho->range_size;
+}
+
+static void cb_parse_vbnv(unsigned char *ptr, struct sysinfo_t *info)
+{
+	struct lb_range *vbnv = (struct lb_range *)ptr;
+
+	info->vbnv_start = vbnv->range_start;
+	info->vbnv_size = vbnv->range_size;
+}
+
+static void cb_parse_cbmem_entry(unsigned char *ptr, struct sysinfo_t *info)
+{
+	struct cb_cbmem_entry *entry = (struct cb_cbmem_entry *)ptr;
+
+	if (entry->id != CBMEM_ID_SMBIOS)
+		return;
+
+	info->smbios_start = entry->address;
+	info->smbios_size = entry->entry_size;
+}
+
+static void cb_parse_gpios(unsigned char *ptr, struct sysinfo_t *info)
+{
+	int i;
+	struct cb_gpios *gpios = (struct cb_gpios *)ptr;
+
+	info->num_gpios = (gpios->count < SYSINFO_MAX_GPIOS) ?
+				(gpios->count) : SYSINFO_MAX_GPIOS;
+
+	for (i = 0; i < info->num_gpios; i++)
+		info->gpios[i] = gpios->gpios[i];
+}
+
+static void cb_parse_vdat(unsigned char *ptr, struct sysinfo_t *info)
+{
+	struct lb_range *vdat = (struct lb_range *)ptr;
+
+	info->vdat_addr = map_sysmem(vdat->range_start, vdat->range_size);
+	info->vdat_size = vdat->range_size;
+}
+
+static void cb_parse_mac_addresses(unsigned char *ptr,
+				   struct sysinfo_t *info)
+{
+	struct cb_macs *macs = (struct cb_macs *)ptr;
+	int i;
+
+	info->num_macs = (macs->count < ARRAY_SIZE(info->macs)) ?
+		macs->count : ARRAY_SIZE(info->macs);
+
+	for (i = 0; i < info->num_macs; i++)
+		info->macs[i] = macs->mac_addrs[i];
+}
+
+static void cb_parse_tstamp(void *ptr, struct sysinfo_t *info)
+{
+	struct cb_cbmem_tab *const cbmem = ptr;
+
+	info->tstamp_table = map_sysmem(cbmem->cbmem_tab, 0);
+}
+
+static void cb_parse_cbmem_cons(void *ptr, struct sysinfo_t *info)
+{
+	struct cb_cbmem_tab *const cbmem = ptr;
+
+	info->cbmem_cons = map_sysmem(cbmem->cbmem_tab, 0);
+}
+
+static void cb_parse_acpi_gnvs(unsigned char *ptr, struct sysinfo_t *info)
+{
+	struct cb_cbmem_tab *const cbmem = (struct cb_cbmem_tab *)ptr;
+
+	info->acpi_gnvs = map_sysmem(cbmem->cbmem_tab, 0);
+}
+
+static void cb_parse_board_id(unsigned char *ptr, struct sysinfo_t *info)
+{
+	struct cb_board_id *const cbbid = (struct cb_board_id *)ptr;
+
+	info->board_id = cbbid->board_id;
+}
+
+static void cb_parse_ram_code(unsigned char *ptr, struct sysinfo_t *info)
+{
+	struct cb_ram_code *const ram_code = (struct cb_ram_code *)ptr;
+
+	info->ram_code = ram_code->ram_code;
+}
+
+static void cb_parse_optiontable(void *ptr, struct sysinfo_t *info)
+{
+	/* ptr points to a coreboot table entry and is already virtual */
+	info->option_table = ptr;
+}
+
+static void cb_parse_checksum(void *ptr, struct sysinfo_t *info)
+{
+	struct cb_cmos_checksum *cmos_cksum = ptr;
+
+	info->cmos_range_start = cmos_cksum->range_start;
+	info->cmos_range_end = cmos_cksum->range_end;
+	info->cmos_checksum_location = cmos_cksum->location;
+}
+
+static void cb_parse_framebuffer(void *ptr, struct sysinfo_t *info)
+{
+	/* ptr points to a coreboot table entry and is already virtual */
+	info->framebuffer = ptr;
+}
+
+static void cb_parse_string(unsigned char *ptr, char **info)
+{
+	*info = (char *)((struct cb_string *)ptr)->string;
+}
+
+static void cb_parse_wifi_calibration(void *ptr, struct sysinfo_t *info)
+{
+	struct cb_cbmem_tab *const cbmem = (struct cb_cbmem_tab *)ptr;
+
+	info->wifi_calibration = map_sysmem(cbmem->cbmem_tab, 0);
+}
+
+static void cb_parse_ramoops(void *ptr, struct sysinfo_t *info)
+{
+	struct lb_range *ramoops = (struct lb_range *)ptr;
+
+	info->ramoops_buffer = ramoops->range_start;
+	info->ramoops_buffer_size = ramoops->range_size;
+}
+
+static void cb_parse_mtc(void *ptr, struct sysinfo_t *info)
+{
+	struct lb_range *mtc = (struct lb_range *)ptr;
+
+	info->mtc_start = mtc->range_start;
+	info->mtc_size = mtc->range_size;
+}
+
+static void cb_parse_spi_flash(void *ptr, struct sysinfo_t *info)
+{
+	struct cb_spi_flash *flash = (struct cb_spi_flash *)ptr;
+
+	info->spi_flash.size = flash->flash_size;
+	info->spi_flash.sector_size = flash->sector_size;
+	info->spi_flash.erase_cmd = flash->erase_cmd;
+}
+
+static void cb_parse_boot_media_params(unsigned char *ptr,
+				       struct sysinfo_t *info)
+{
+	struct cb_boot_media_params *const bmp =
+			(struct cb_boot_media_params *)ptr;
+
+	info->fmap_offset = bmp->fmap_offset;
+	info->cbfs_offset = bmp->cbfs_offset;
+	info->cbfs_size = bmp->cbfs_size;
+	info->boot_media_size = bmp->boot_media_size;
+}
+
+static void cb_parse_vpd(void *ptr, struct sysinfo_t *info)
+{
+	struct cb_cbmem_tab *const cbmem = (struct cb_cbmem_tab *)ptr;
+
+	info->chromeos_vpd = map_sysmem(cbmem->cbmem_tab, 0);
+}
+
+static void cb_parse_tsc_info(void *ptr, struct sysinfo_t *info)
+{
+	const struct cb_tsc_info *tsc_info = ptr;
+
+	if (tsc_info->freq_khz == 0)
+		return;
+
+	/* Honor the TSC frequency passed to the payload */
+	info->cpu_khz = tsc_info->freq_khz;
+}
+
+static void cb_parse_x86_rom_var_mtrr(void *ptr, struct sysinfo_t *info)
+{
+	struct cb_x86_rom_mtrr *rom_mtrr = ptr;
+
+	info->x86_rom_var_mtrr_index = rom_mtrr->index;
+}
+
+static void cb_parse_mrc_cache(void *ptr, struct sysinfo_t *info)
+{
+	struct cb_cbmem_tab *const cbmem = (struct cb_cbmem_tab *)ptr;
+
+	info->mrc_cache = map_sysmem(cbmem->cbmem_tab, 0);
+}
+
+__weak void cb_parse_unhandled(u32 tag, unsigned char *ptr)
+{
+}
+
+static int cb_parse_header(void *addr, int len, struct sysinfo_t *info)
+{
+	unsigned char *ptr = addr;
+	struct cb_header *header;
+	int i;
+
+	header = (struct cb_header *)ptr;
+	if (!header->table_bytes)
+		return 0;
+
+	/* Make sure the checksums match */
+	if (!ip_checksum_ok(header, sizeof(*header)))
+		return -1;
+
+	if (compute_ip_checksum(ptr + sizeof(*header), header->table_bytes) !=
+	    header->table_checksum)
+		return -1;
+
+	info->header = header;
+
+	/*
+	 * Board straps represented by numerical values are small numbers.
+	 * Preset them to an invalid value in case the firmware does not
+	 * supply the info.
+	 */
+	info->board_id = ~0;
+	info->ram_code = ~0;
+
+	/* Now, walk the tables */
+	ptr += header->header_bytes;
+
+	/* Inintialize some fields to sentinel values */
+	info->vbnv_start = info->vbnv_size = (uint32_t)(-1);
+
+	for (i = 0; i < header->table_entries; i++) {
+		struct cb_record *rec = (struct cb_record *)ptr;
+
+		/* We only care about a few tags here (maybe more later) */
+		switch (rec->tag) {
+		case CB_TAG_FORWARD:
+			return cb_parse_header(
+				(void *)(unsigned long)
+				((struct cb_forward *)rec)->forward,
+				len, info);
+			continue;
+		case CB_TAG_MEMORY:
+			cb_parse_memory(ptr, info);
+			break;
+		case CB_TAG_SERIAL:
+			cb_parse_serial(ptr, info);
+			break;
+		case CB_TAG_VERSION:
+			cb_parse_string(ptr, &info->cb_version);
+			break;
+		case CB_TAG_EXTRA_VERSION:
+			cb_parse_string(ptr, &info->extra_version);
+			break;
+		case CB_TAG_BUILD:
+			cb_parse_string(ptr, &info->build);
+			break;
+		case CB_TAG_COMPILE_TIME:
+			cb_parse_string(ptr, &info->compile_time);
+			break;
+		case CB_TAG_COMPILE_BY:
+			cb_parse_string(ptr, &info->compile_by);
+			break;
+		case CB_TAG_COMPILE_HOST:
+			cb_parse_string(ptr, &info->compile_host);
+			break;
+		case CB_TAG_COMPILE_DOMAIN:
+			cb_parse_string(ptr, &info->compile_domain);
+			break;
+		case CB_TAG_COMPILER:
+			cb_parse_string(ptr, &info->compiler);
+			break;
+		case CB_TAG_LINKER:
+			cb_parse_string(ptr, &info->linker);
+			break;
+		case CB_TAG_ASSEMBLER:
+			cb_parse_string(ptr, &info->assembler);
+			break;
+		case CB_TAG_CMOS_OPTION_TABLE:
+			cb_parse_optiontable(ptr, info);
+			break;
+		case CB_TAG_OPTION_CHECKSUM:
+			cb_parse_checksum(ptr, info);
+			break;
+		/*
+		 * FIXME we should warn on serial if coreboot set up a
+		 * framebuffer buf the payload does not know about it.
+		 */
+		case CB_TAG_FRAMEBUFFER:
+			cb_parse_framebuffer(ptr, info);
+			break;
+		case CB_TAG_MAINBOARD:
+			info->mainboard = (struct cb_mainboard *)ptr;
+			break;
+		case CB_TAG_GPIO:
+			cb_parse_gpios(ptr, info);
+			break;
+		case CB_TAG_VDAT:
+			cb_parse_vdat(ptr, info);
+			break;
+		case CB_TAG_VBNV:
+			cb_parse_vbnv(ptr, info);
+			break;
+		case CB_TAG_VBOOT_HANDOFF:
+			cb_parse_vboot_handoff(ptr, info);
+			break;
+		case CB_TAG_MAC_ADDRS:
+			cb_parse_mac_addresses(ptr, info);
+			break;
+		case CB_TAG_SERIALNO:
+			cb_parse_string(ptr, &info->serialno);
+			break;
+		case CB_TAG_TIMESTAMPS:
+			cb_parse_tstamp(ptr, info);
+			break;
+		case CB_TAG_CBMEM_CONSOLE:
+			cb_parse_cbmem_cons(ptr, info);
+			break;
+		case CB_TAG_ACPI_GNVS:
+			cb_parse_acpi_gnvs(ptr, info);
+			break;
+		case CB_TAG_CBMEM_ENTRY:
+			cb_parse_cbmem_entry(ptr, info);
+			break;
+		case CB_TAG_BOARD_ID:
+			cb_parse_board_id(ptr, info);
+			break;
+		case CB_TAG_RAM_CODE:
+			cb_parse_ram_code(ptr, info);
+			break;
+		case CB_TAG_WIFI_CALIBRATION:
+			cb_parse_wifi_calibration(ptr, info);
+			break;
+		case CB_TAG_RAM_OOPS:
+			cb_parse_ramoops(ptr, info);
+			break;
+		case CB_TAG_SPI_FLASH:
+			cb_parse_spi_flash(ptr, info);
+			break;
+		case CB_TAG_MTC:
+			cb_parse_mtc(ptr, info);
+			break;
+		case CB_TAG_BOOT_MEDIA_PARAMS:
+			cb_parse_boot_media_params(ptr, info);
+			break;
+		case CB_TAG_TSC_INFO:
+			cb_parse_tsc_info(ptr, info);
+			break;
+		case CB_TAG_VPD:
+			cb_parse_vpd(ptr, info);
+			break;
+		case CB_TAG_X86_ROM_MTRR:
+			cb_parse_x86_rom_var_mtrr(rec, info);
+			break;
+		case CB_TAG_MRC_CACHE:
+			cb_parse_mrc_cache(rec, info);
+			break;
+		default:
+			cb_parse_unhandled(rec->tag, ptr);
+			break;
+		}
+
+		ptr += rec->size;
+	}
+
+	return 1;
+}
+
+/* == Architecture specific == */
+/* This is the x86 specific stuff */
+
+int get_coreboot_info(struct sysinfo_t *info)
+{
+	long addr;
+	int ret;
+
+	addr = locate_coreboot_table();
+	if (addr < 0)
+		return addr;
+	ret = cb_parse_header((void *)addr, 0x1000, info);
+	if (!ret)
+		return -ENOENT;
+	gd->arch.coreboot_table = addr;
+	gd->flags |= GD_FLG_SKIP_LL_INIT;
+
+	return 0;
+}
+
+const struct sysinfo_t *cb_get_sysinfo(void)
+{
+	if (!ll_boot_init())
+		return &lib_sysinfo;
+
+	return NULL;
+}
diff --git a/arch/x86/lib/fsp/fsp_graphics.c b/arch/x86/lib/fsp/fsp_graphics.c
index cc909e0..02fd05c 100644
--- a/arch/x86/lib/fsp/fsp_graphics.c
+++ b/arch/x86/lib/fsp/fsp_graphics.c
@@ -87,7 +87,7 @@
 	int ret;
 
 	if (!ll_boot_init())
-		return 0;
+		return -ENODEV;
 
 	printf("Video: ");
 
diff --git a/arch/x86/lib/fsp2/fsp_init.c b/arch/x86/lib/fsp2/fsp_init.c
index 85cae54..5afdce1 100644
--- a/arch/x86/lib/fsp2/fsp_init.c
+++ b/arch/x86/lib/fsp2/fsp_init.c
@@ -84,7 +84,8 @@
 	struct cbfs_priv *cbfs;
 	int ret;
 
-	ret = cbfs_init_mem(map_base + cbfs_base, &cbfs);
+	ret = cbfs_init_mem(map_base + cbfs_base, CBFS_SIZE_UNKNOWN, true,
+			    &cbfs);
 	if (ret)
 		return ret;
 	if (!ret) {
diff --git a/arch/x86/lib/fsp2/fsp_support.c b/arch/x86/lib/fsp2/fsp_support.c
index 503f97d..b2c7658 100644
--- a/arch/x86/lib/fsp2/fsp_support.c
+++ b/arch/x86/lib/fsp2/fsp_support.c
@@ -6,6 +6,7 @@
 
 #include <common.h>
 #include <dm.h>
+#include <init.h>
 #include <log.h>
 #include <spi_flash.h>
 #include <asm/fsp/fsp_support.h>
@@ -114,6 +115,9 @@
 	struct fsp_notify_params *params_ptr;
 	u32 status;
 
+	if (!ll_boot_init())
+		return 0;
+
 	if (!fsp_hdr)
 		fsp_hdr = gd->arch.fsp_s_hdr;
 
diff --git a/arch/x86/lib/init_helpers.c b/arch/x86/lib/init_helpers.c
index 066dc40..67401b9 100644
--- a/arch/x86/lib/init_helpers.c
+++ b/arch/x86/lib/init_helpers.c
@@ -18,6 +18,9 @@
 		 IS_ENABLED(CONFIG_FSP_VERSION2);
 	int ret;
 
+	if (!ll_boot_init())
+		return 0;
+
 	do_mtrr &= !IS_ENABLED(CONFIG_FSP_VERSION1) &&
 		!IS_ENABLED(CONFIG_SYS_SLIMBOOTLOADER);
 
@@ -31,9 +34,6 @@
 			return ret;
 	}
 
-	if (!ll_boot_init())
-		return 0;
-
 	/* Initialise the CPU cache(s) */
 	return init_cache();
 }
diff --git a/arch/x86/lib/spl.c b/arch/x86/lib/spl.c
index 1bae1f4..b18c1cd 100644
--- a/arch/x86/lib/spl.c
+++ b/arch/x86/lib/spl.c
@@ -96,7 +96,7 @@
 	}
 #endif
 	preloader_console_init();
-#ifndef CONFIG_TPL
+#if !defined(CONFIG_TPL) && !CONFIG_IS_ENABLED(CPU)
 	ret = print_cpuinfo();
 	if (ret) {
 		debug("%s: print_cpuinfo() failed\n", __func__);
diff --git a/arch/x86/lib/zimage.c b/arch/x86/lib/zimage.c
index 602788e..90fc8a4 100644
--- a/arch/x86/lib/zimage.c
+++ b/arch/x86/lib/zimage.c
@@ -18,6 +18,7 @@
 #include <bootm.h>
 #include <command.h>
 #include <env.h>
+#include <init.h>
 #include <irq_func.h>
 #include <log.h>
 #include <malloc.h>
@@ -28,6 +29,7 @@
 #include <asm/byteorder.h>
 #include <asm/bootm.h>
 #include <asm/bootparam.h>
+#include <asm/global_data.h>
 #ifdef CONFIG_SYS_COREBOOT
 #include <asm/arch/timestamp.h>
 #endif
@@ -35,6 +37,8 @@
 #include <linux/ctype.h>
 #include <linux/libfdt.h>
 
+DECLARE_GLOBAL_DATA_PTR;
+
 /*
  * Memory lay-out:
  *
@@ -309,8 +313,13 @@
 	int bootproto = get_boot_protocol(hdr, false);
 
 	log_debug("Setup E820 entries\n");
-	setup_base->e820_entries = install_e820_map(
-		ARRAY_SIZE(setup_base->e820_map), setup_base->e820_map);
+	if (ll_boot_init()) {
+		setup_base->e820_entries = install_e820_map(
+			ARRAY_SIZE(setup_base->e820_map), setup_base->e820_map);
+	} else if (IS_ENABLED(CONFIG_COREBOOT_SYSINFO)) {
+		setup_base->e820_entries = cb_install_e820_map(
+			ARRAY_SIZE(setup_base->e820_map), setup_base->e820_map);
+	}
 
 	if (bootproto == 0x0100) {
 		setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC;
diff --git a/board/coreboot/coreboot/coreboot.c b/board/coreboot/coreboot/coreboot.c
index 9aafb89..175d3ce 100644
--- a/board/coreboot/coreboot/coreboot.c
+++ b/board/coreboot/coreboot/coreboot.c
@@ -4,7 +4,7 @@
  */
 
 #include <common.h>
-#include <asm/arch/sysinfo.h>
+#include <asm/cb_sysinfo.h>
 #include <asm/global_data.h>
 #include <init.h>
 #include <smbios.h>
diff --git a/board/google/chromebook_coral/coral.c b/board/google/chromebook_coral/coral.c
index f9fb3f1..3f9235c 100644
--- a/board/google/chromebook_coral/coral.c
+++ b/board/google/chromebook_coral/coral.c
@@ -3,9 +3,12 @@
  * Copyright 2019 Google LLC
  */
 
+#define LOG_CATEGORY	UCLASS_SYSINFO
+
 #include <common.h>
 #include <bloblist.h>
 #include <command.h>
+#include <cros_ec.h>
 #include <dm.h>
 #include <log.h>
 #include <sysinfo.h>
@@ -15,6 +18,7 @@
 #include <asm/intel_gnvs.h>
 #include <asm/intel_pinctrl.h>
 #include <dm/acpi.h>
+#include <linux/delay.h>
 #include "variant_gpio.h"
 
 struct cros_gpio_info {
@@ -29,10 +33,125 @@
 	return 0;
 }
 
+static int get_memconfig(struct udevice *dev)
+{
+	struct gpio_desc gpios[4];
+	int cfg;
+	int ret;
+
+	ret = gpio_request_list_by_name(dev, "memconfig-gpios", gpios,
+					ARRAY_SIZE(gpios),
+					GPIOD_IS_IN | GPIOD_PULL_UP);
+	if (ret < 0) {
+		log_debug("Cannot get GPIO list '%s' (%d)\n", dev->name, ret);
+		return ret;
+	}
+
+	/* Give the lines time to settle */
+	udelay(10);
+
+	ret = dm_gpio_get_values_as_int(gpios, ARRAY_SIZE(gpios));
+	if (ret < 0)
+		return log_msg_ret("get", ret);
+	cfg = ret;
+
+	ret = gpio_free_list(dev, gpios, ARRAY_SIZE(gpios));
+	if (ret)
+		return log_msg_ret("free", ret);
+
+	return cfg;
+}
+
+/**
+ * get_skuconfig() - Get the SKU number either from pins or the EC
+ *
+ * Two options are supported:
+ *     skuconfig-gpios - two pins in the device tree (tried first)
+ *     EC              - reading from the EC (backup)
+ *
+ * @dev: sysinfo device to use
+ * @return SKU ID, or -ve error if not found
+ */
+static int get_skuconfig(struct udevice *dev)
+{
+	struct gpio_desc gpios[2];
+	int cfg;
+	int ret;
+
+	ret = gpio_request_list_by_name(dev, "skuconfig-gpios", gpios,
+					ARRAY_SIZE(gpios),
+					GPIOD_IS_IN);
+	if (ret != ARRAY_SIZE(gpios)) {
+		struct udevice *cros_ec;
+
+		log_debug("Cannot get GPIO list '%s' (%d)\n", dev->name, ret);
+
+		/* Try the EC */
+		ret = uclass_first_device_err(UCLASS_CROS_EC, &cros_ec);
+		if (ret < 0) {
+			log_err("Cannot find EC for SKU details\n");
+			return log_msg_ret("sku", ret);
+		}
+		ret = cros_ec_get_sku_id(cros_ec);
+		if (ret < 0) {
+			log_err("Cannot read SKU details\n");
+			return log_msg_ret("sku", ret);
+		}
+
+		return ret;
+	}
+
+	ret = dm_gpio_get_values_as_int_base3(gpios, ARRAY_SIZE(gpios));
+	if (ret < 0)
+		return log_msg_ret("get", ret);
+	cfg = ret;
+
+	ret = gpio_free_list(dev, gpios, ARRAY_SIZE(gpios));
+	if (ret)
+		return log_msg_ret("free", ret);
+
+	return cfg;
+}
+
-/* This function is needed if CONFIG_CMDLINE is not enabled */
-int board_run_command(const char *cmdline)
+static int coral_get_str(struct udevice *dev, int id, size_t size, char *val)
 {
-	printf("No command line\n");
+	int ret;
+
+	if (IS_ENABLED(CONFIG_SPL_BUILD))
+		return -ENOSYS;
+
+	switch (id) {
+	case SYSINFO_ID_SMBIOS_SYSTEM_VERSION:
+	case SYSINFO_ID_SMBIOS_BASEBOARD_VERSION: {
+		ret = get_skuconfig(dev);
+
+		if (ret < 0)
+			return ret;
+		if (size < 15)
+			return -ENOSPC;
+		sprintf(val, "rev%d", ret);
+		break;
+	}
+	case SYSINFO_ID_BOARD_MODEL: {
+		int mem_config, sku_config;
+		const char *model;
+
+		ret = get_memconfig(dev);
+		if (ret < 0)
+			log_warning("Unable to read memconfig (err=%d)\n", ret);
+		mem_config = ret;
+		ret = get_skuconfig(dev);
+		if (ret < 0)
+			log_warning("Unable to read skuconfig (err=%d)\n", ret);
+		sku_config = ret;
+		model = fdt_getprop(gd->fdt_blob, 0, "model", NULL);
+		snprintf(val, size, "%s (memconfig %d, SKU %d)", model,
+			 mem_config, sku_config);
+		break;
+	}
+	default:
+		return -ENOENT;
+	}
 
 	return 0;
 }
@@ -45,12 +164,15 @@
 	int ret;
 
 	ret = gpio_request_by_name((struct udevice *)dev, prop, 0, &desc, 0);
-	if (ret == -ENOTBLK)
+	if (ret == -ENOTBLK) {
 		info->gpio_num = CROS_GPIO_VIRTUAL;
-	else if (ret)
+		log_debug("GPIO '%s' is virtual\n", prop);
+	} else if (ret) {
 		return log_msg_ret("gpio", ret);
-	else
+	} else {
 		info->gpio_num = desc.offset;
+		dm_gpio_free((struct udevice *)dev, &desc);
+	}
 	info->linux_name = dev_read_string(desc.dev, "linux-name");
 	if (!info->linux_name)
 		return log_msg_ret("linux-name", -ENOENT);
@@ -63,6 +185,8 @@
 	}
 	info->flags = desc.flags & GPIOD_ACTIVE_LOW ? CROS_GPIO_ACTIVE_LOW :
 		CROS_GPIO_ACTIVE_HIGH;
+	if (!ret)
+		dm_gpio_free(desc.dev, &desc);
 
 	return 0;
 }
@@ -81,11 +205,11 @@
 	ret = chromeos_get_gpio(dev, "write-protect-gpios", CROS_GPIO_WP,
 				&info[1]);
 	if (ret)
-		return log_msg_ret("rec", ret);
+		return log_msg_ret("wp", ret);
 	ret = chromeos_get_gpio(dev, "phase-enforce-gpios", CROS_GPIO_PE,
 				&info[2]);
 	if (ret)
-		return log_msg_ret("rec", ret);
+		return log_msg_ret("phase", ret);
 	acpigen_write_scope(ctx, "\\");
 	acpigen_write_name(ctx, "OIPG");
 	acpigen_write_package(ctx, count);
@@ -145,6 +269,7 @@
 };
 
 struct sysinfo_ops coral_sysinfo_ops = {
+	.get_str	= coral_get_str,
 };
 
 #if !CONFIG_IS_ENABLED(OF_PLATDATA)
diff --git a/board/google/chromebook_coral/variant_gpio.h b/board/google/chromebook_coral/variant_gpio.h
index f516d88..403e241 100644
--- a/board/google/chromebook_coral/variant_gpio.h
+++ b/board/google/chromebook_coral/variant_gpio.h
@@ -34,12 +34,6 @@
 /* Determine if board is in final shipping mode. */
 #define GPIO_SHIP_MODE	GPIO_10
 
-/*  Memory SKU GPIOs. */
-#define MEM_CONFIG3	GPIO_45
-#define MEM_CONFIG2	GPIO_38
-#define MEM_CONFIG1	GPIO_102
-#define MEM_CONFIG0	GPIO_101
-
 /* DMIC_CONFIG_PIN: High for 1-DMIC and low for 4-DMIC's */
 #define DMIC_CONFIG_PIN	GPIO_17
 
diff --git a/cmd/Kconfig b/cmd/Kconfig
index e4bb1d4..8ab34f3 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -2251,6 +2251,15 @@
 	  for some PowerPC processors. For details please see the
 	  documentation in doc/README.bedbug.
 
+config CMD_CBSYSINFO
+	bool "cbsysinfo"
+	depends on X86
+	default y if SYS_COREBOOT
+	help
+	  This provides information about the coreboot sysinfo table stored in
+	  memory by coreboot before jumping to U-Boot. It can be useful for
+	  debugging the beaaviour of coreboot or U-Boot.
+
 config CMD_DIAG
 	bool "diag - Board diagnostics"
 	help
diff --git a/cmd/acpi.c b/cmd/acpi.c
index 157261b..e5b9a17 100644
--- a/cmd/acpi.c
+++ b/cmd/acpi.c
@@ -187,10 +187,12 @@
 	return 0;
 }
 
+#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";
+#endif
 
 U_BOOT_CMD_WITH_SUBCMDS(acpi, "ACPI tables", acpi_help_text,
 	U_BOOT_SUBCMD_MKENT(list, 1, 1, do_acpi_list),
diff --git a/cmd/bloblist.c b/cmd/bloblist.c
index 97b5734..21e7ff6 100644
--- a/cmd/bloblist.c
+++ b/cmd/bloblist.c
@@ -29,9 +29,11 @@
 	return 0;
 }
 
+#ifdef CONFIG_SYS_LONGHELP
 static char bloblist_help_text[] =
 	"info   - show information about the bloblist\n"
 	"bloblist list   - list blobs in the bloblist";
+#endif
 
 U_BOOT_CMD_WITH_SUBCMDS(bloblist, "Bloblists", bloblist_help_text,
 	U_BOOT_SUBCMD_MKENT(info, 1, 1, do_bloblist_info),
diff --git a/cmd/host.c b/cmd/host.c
index 1d21f79..6aa3d91 100644
--- a/cmd/host.c
+++ b/cmd/host.c
@@ -41,17 +41,35 @@
 static int do_host_bind(struct cmd_tbl *cmdtp, int flag, int argc,
 			char *const argv[])
 {
-	if (argc < 2 || argc > 3)
-		return CMD_RET_USAGE;
+	bool removable = false;
+	const char *dev_str;
+	char *file;
 	char *ep;
-	char *dev_str = argv[1];
-	char *file = argc >= 3 ? argv[2] : NULL;
-	int dev = simple_strtoul(dev_str, &ep, 16);
+	int dev;
+
+	/* Skip 'bind' */
+	argc--;
+	argv++;
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	if (!strcmp(argv[0], "-r")) {
+		removable = true;
+		argc--;
+		argv++;
+	}
+
+	if (argc > 2)
+		return CMD_RET_USAGE;
+	dev_str = argv[0];
+	dev = simple_strtoul(dev_str, &ep, 16);
 	if (*ep) {
 		printf("** Bad device specification %s **\n", dev_str);
 		return CMD_RET_USAGE;
 	}
-	return !!host_dev_bind(dev, file);
+	file = argc > 1 ? argv[1] : NULL;
+
+	return !!host_dev_bind(dev, file, removable);
 }
 
 static int do_host_info(struct cmd_tbl *cmdtp, int flag, int argc,
@@ -146,7 +164,7 @@
 	U_BOOT_CMD_MKENT(ls, 3, 0, do_host_ls, "", ""),
 	U_BOOT_CMD_MKENT(save, 6, 0, do_host_save, "", ""),
 	U_BOOT_CMD_MKENT(size, 3, 0, do_host_size, "", ""),
-	U_BOOT_CMD_MKENT(bind, 3, 0, do_host_bind, "", ""),
+	U_BOOT_CMD_MKENT(bind, 4, 0, do_host_bind, "", ""),
 	U_BOOT_CMD_MKENT(info, 3, 0, do_host_info, "", ""),
 	U_BOOT_CMD_MKENT(dev, 0, 1, do_host_dev, "", ""),
 };
@@ -178,7 +196,8 @@
 	"host save hostfs - <addr> <filename> <bytes> [<offset>] - "
 		"save a file to host\n"
 	"host size hostfs - <filename> - determine size of file on host\n"
-	"host bind <dev> [<filename>] - bind \"host\" device to file\n"
+	"host bind [-r] <dev> [<filename>] - bind \"host\" device to file\n"
+	"     -r = mark as removable\n"
 	"host info [<dev>]            - show device binding & info\n"
 	"host dev [<dev>] - Set or retrieve the current host device\n"
 	"host commands use the \"hostfs\" device. The \"host\" device is used\n"
diff --git a/cmd/version.c b/cmd/version.c
index 3686b87..685b458 100644
--- a/cmd/version.c
+++ b/cmd/version.c
@@ -9,7 +9,7 @@
 #include <version.h>
 #include <linux/compiler.h>
 #ifdef CONFIG_SYS_COREBOOT
-#include <asm/arch/sysinfo.h>
+#include <asm/cb_sysinfo.h>
 #endif
 
 const char __weak version_string[] = U_BOOT_VERSION_STRING;
diff --git a/cmd/x86/Makefile b/cmd/x86/Makefile
index 144b1cf..5f82204 100644
--- a/cmd/x86/Makefile
+++ b/cmd/x86/Makefile
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0+
 
+obj-$(CONFIG_CMD_CBSYSINFO) += cbsysinfo.o
 obj-y += mtrr.o
 obj-$(CONFIG_CMD_EXCEPTION) += exception.o
 obj-$(CONFIG_USE_HOB) += hob.o
diff --git a/cmd/x86/cbsysinfo.c b/cmd/x86/cbsysinfo.c
new file mode 100644
index 0000000..a0db0ad
--- /dev/null
+++ b/cmd/x86/cbsysinfo.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <asm/cb_sysinfo.h>
+#include <command.h>
+#include <console.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static void cbprompt(const char *name)
+{
+	for (; *name == '>'; name++)
+		puts("   ");
+	printf("%-12s: ", name);
+}
+
+static void print_dec(const char *name, int value)
+{
+	cbprompt(name);
+	printf(value > 9 ? "0d%d\n" : "%d\n", value);
+}
+
+static void print_hex(const char *name, int value)
+{
+	cbprompt(name);
+	printf("%x\n", value);
+}
+
+static void print_addr(const char *name, ulong value)
+{
+	cbprompt(name);
+	printf("%08lx\n", value);
+}
+
+static void print_addr64(const char *name, u64 value)
+{
+	cbprompt(name);
+	printf("%16llx\n", value);
+}
+
+static void print_ptr(const char *name, const void *value)
+{
+	cbprompt(name);
+	printf("%p\n", value);
+}
+
+static void print_str(const char *name, const char *value)
+{
+	if (value) {
+		cbprompt(name);
+		printf("%s\n", value);
+	}
+}
+
+static void print_idx(const char *name, uint idx, const u8 *strings)
+{
+	const char *ptr;
+
+	cbprompt(name);
+	ptr = (char *)strings + idx;
+	printf("%d: %s\n", idx, ptr ? ptr : "(unknown)");
+}
+
+static const char *const cb_mem_name[] = {
+	NULL,
+	"ram",
+	"reserved",
+	"acpi",
+	"nvs",
+	"unusable",
+	"vendor",
+};
+
+static const char *get_mem_name(int tag)
+{
+	if (tag >= CB_MEM_RAM && tag <= CB_MEM_VENDOR_RSVD)
+		return cb_mem_name[tag];
+
+	if (tag == CB_MEM_TABLE)
+		return "table";
+
+	return "(unknown)";
+}
+
+static const struct timestamp_id_to_name {
+	uint id;
+	const char *name;
+} timestamp_ids[] = {
+	/* Marker to report base_time */
+	{ 0,			"1st timestamp" },
+	{ TS_START_ROMSTAGE,	"start of romstage" },
+	{ TS_BEFORE_INITRAM,	"before ram initialization" },
+	{ TS_AFTER_INITRAM,	"after ram initialization" },
+	{ TS_END_ROMSTAGE,	"end of romstage" },
+	{ TS_START_VBOOT,	"start of verified boot" },
+	{ TS_END_VBOOT,		"end of verified boot" },
+	{ TS_START_COPYRAM,	"starting to load ramstage" },
+	{ TS_END_COPYRAM,	"finished loading ramstage" },
+	{ TS_START_RAMSTAGE,	"start of ramstage" },
+	{ TS_START_BOOTBLOCK,	"start of bootblock" },
+	{ TS_END_BOOTBLOCK,	"end of bootblock" },
+	{ TS_START_COPYROM,	"starting to load romstage" },
+	{ TS_END_COPYROM,	"finished loading romstage" },
+	{ TS_START_ULZMA,	"starting LZMA decompress (ignore for x86)" },
+	{ TS_END_ULZMA,		"finished LZMA decompress (ignore for x86)" },
+	{ TS_START_ULZ4F,	"starting LZ4 decompress (ignore for x86)" },
+	{ TS_END_ULZ4F,		"finished LZ4 decompress (ignore for x86)" },
+	{ TS_DEVICE_ENUMERATE,	"device enumeration" },
+	{ TS_DEVICE_CONFIGURE,	"device configuration" },
+	{ TS_DEVICE_ENABLE,	"device enable" },
+	{ TS_DEVICE_INITIALIZE,	"device initialization" },
+	{ TS_DEVICE_DONE,	"device setup done" },
+	{ TS_CBMEM_POST,	"cbmem post" },
+	{ TS_WRITE_TABLES,	"write tables" },
+	{ TS_FINALIZE_CHIPS,	"finalize chips" },
+	{ TS_LOAD_PAYLOAD,	"load payload" },
+	{ TS_ACPI_WAKE_JUMP,	"ACPI wake jump" },
+	{ TS_SELFBOOT_JUMP,	"selfboot jump" },
+
+	{ TS_START_COPYVER,	"starting to load verstage" },
+	{ TS_END_COPYVER,	"finished loading verstage" },
+	{ TS_START_TPMINIT,	"starting to initialize TPM" },
+	{ TS_END_TPMINIT,	"finished TPM initialization" },
+	{ TS_START_VERIFY_SLOT,	"starting to verify keyblock/preamble (RSA)" },
+	{ TS_END_VERIFY_SLOT,	"finished verifying keyblock/preamble (RSA)" },
+	{ TS_START_HASH_BODY,	"starting to verify body (load+SHA2+RSA) " },
+	{ TS_DONE_LOADING,	"finished loading body (ignore for x86)" },
+	{ TS_DONE_HASHING,	"finished calculating body hash (SHA2)" },
+	{ TS_END_HASH_BODY,	"finished verifying body signature (RSA)" },
+
+	{ TS_START_COPYVPD,	"starting to load Chrome OS VPD" },
+	{ TS_END_COPYVPD_RO,	"finished loading Chrome OS VPD (RO)" },
+	{ TS_END_COPYVPD_RW,	"finished loading Chrome OS VPD (RW)" },
+
+	{ TS_U_BOOT_INITTED,	"U-Boot start" },
+	{ TS_RO_PARAMS_INIT,	"RO parameter init" },
+	{ TS_RO_VB_INIT,	"RO vboot init" },
+	{ TS_RO_VB_SELECT_FIRMWARE,		"RO vboot select firmware" },
+	{ TS_RO_VB_SELECT_AND_LOAD_KERNEL,	"RO vboot select&load kernel" },
+	{ TS_RW_VB_SELECT_AND_LOAD_KERNEL,	"RW vboot select&load kernel" },
+	{ TS_VB_SELECT_AND_LOAD_KERNEL,		"vboot select&load kernel" },
+	{ TS_VB_EC_VBOOT_DONE,	"finished EC verification" },
+	{ TS_VB_STORAGE_INIT_DONE, "finished storage device initialization" },
+	{ TS_VB_READ_KERNEL_DONE, "finished reading kernel from disk" },
+	{ TS_VB_VBOOT_DONE,	"finished vboot kernel verification" },
+	{ TS_KERNEL_DECOMPRESSION, "starting kernel decompression/relocation" },
+	{ TS_START_KERNEL,	"jumping to kernel" },
+	{ TS_U_BOOT_START_KERNEL,	"just before jump to kernel" },
+
+	/* Intel ME-related timestamps */
+	{ TS_ME_INFORM_DRAM_WAIT, "waiting for ME acknowledgment of raminit"},
+	{ TS_ME_INFORM_DRAM_DONE, "finished waiting for ME response"},
+
+	/* FSP-related timestamps */
+	{ TS_FSP_MEMORY_INIT_START, "calling FspMemoryInit" },
+	{ TS_FSP_MEMORY_INIT_END, "returning from FspMemoryInit" },
+	{ TS_FSP_TEMP_RAM_EXIT_START, "calling FspTempRamExit" },
+	{ TS_FSP_TEMP_RAM_EXIT_END, "returning from FspTempRamExit" },
+	{ TS_FSP_SILICON_INIT_START, "calling FspSiliconInit" },
+	{ TS_FSP_SILICON_INIT_END, "returning from FspSiliconInit" },
+	{ TS_FSP_BEFORE_ENUMERATE, "calling FspNotify(AfterPciEnumeration)" },
+	{ TS_FSP_AFTER_ENUMERATE,
+		 "returning from FspNotify(AfterPciEnumeration)" },
+	{ TS_FSP_BEFORE_FINALIZE, "calling FspNotify(ReadyToBoot)" },
+	{ TS_FSP_AFTER_FINALIZE, "returning from FspNotify(ReadyToBoot)" },
+	{ TS_FSP_BEFORE_END_OF_FIRMWARE, "calling FspNotify(EndOfFirmware)" },
+	{ TS_FSP_AFTER_END_OF_FIRMWARE,
+		"returning from FspNotify(EndOfFirmware)" },
+};
+
+static const char *timestamp_name(uint32_t id)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(timestamp_ids); i++) {
+		if (timestamp_ids[i].id == id)
+			return timestamp_ids[i].name;
+	}
+
+	return "<unknown>";
+}
+
+static void show_table(struct sysinfo_t *info, bool verbose)
+{
+	struct cb_serial *ser = info->serial;
+	int i;
+
+	printf("Coreboot table at %lx, decoded to %p",
+	       gd->arch.coreboot_table, info);
+	if (info->header)
+		printf(", forwarded to %p\n", info->header);
+	printf("\n");
+
+	print_dec("CPU KHz", info->cpu_khz);
+
+	print_addr("Serial I/O port", info->ser_ioport);
+	print_addr(">base", info->ser_base);
+	print_ptr(">pointer", ser);
+	if (ser) {
+		print_hex(">type", ser->type);
+		print_addr(">base", ser->baseaddr);
+		print_dec(">baud", ser->baud);
+		print_hex(">baud", ser->regwidth);
+		print_dec(">input_hz", ser->input_hertz);
+		print_addr(">PCI addr", ser->uart_pci_addr);
+	}
+
+	print_dec("Mem ranges", info->n_memranges);
+	printf("%12s: %-11s        ||   base        ||   size\n", "id", "type");
+	for (i = 0; i < info->n_memranges; i++) {
+		const struct memrange *mr = &info->memrange[i];
+
+		printf("%12d: %02x:%-8s %016llx %016llx\n", i, mr->type,
+		       get_mem_name(mr->type), mr->base, mr->size);
+	}
+	print_ptr("option_table", info->option_table);
+
+	print_hex("CMOS start", info->cmos_range_start);
+	if (info->cmos_range_start) {
+		print_hex(">CMOS end", info->cmos_range_end);
+		print_hex(">CMOS csum loc", info->cmos_checksum_location);
+	}
+
+	print_hex("VBNV start", info->vbnv_start);
+	print_hex("VBNV size", info->vbnv_size);
+
+	print_str("CB version", info->cb_version);
+	print_str(">Extra", info->extra_version);
+	print_str(">Build", info->build);
+	print_str(">Time", info->compile_time);
+	print_str(">By", info->compile_by);
+	print_str(">Host", info->compile_host);
+	print_str(">Domain", info->compile_domain);
+	print_str(">Compiler", info->compiler);
+	print_str(">Linker", info->linker);
+	print_str(">Assembler", info->assembler);
+
+	print_ptr("Framebuffer", info->framebuffer);
+	if (info->framebuffer) {
+		struct cb_framebuffer *fb = info->framebuffer;
+
+		print_addr64(">Phys addr", fb->physical_address);
+		print_dec(">X res", fb->x_resolution);
+		print_dec(">X res", fb->y_resolution);
+		print_hex(">Bytes / line", fb->bytes_per_line);
+		print_dec(">Bpp", fb->bits_per_pixel);
+		printf("   %-12s  red %d/%d, green %d/%d, blue %d/%d, reserved %d/%d\n",
+		       "pos/size", fb->red_mask_pos, fb->red_mask_size,
+		       fb->green_mask_pos, fb->green_mask_size,
+		       fb->blue_mask_pos, fb->blue_mask_size,
+		       fb->reserved_mask_pos, fb->reserved_mask_size);
+	}
+
+	print_dec("GPIOs", info->num_gpios);
+	printf("%12s: %4s %12s %3s %s\n", "id", "port", "polarity", "val",
+	       "name");
+	for (i = 0; i < info->num_gpios; i++) {
+		const struct cb_gpio *gpio = &info->gpios[i];
+		char portstr[4];
+
+		if (gpio->port == 0xffffffff)
+			strcpy(portstr, "-");
+		else
+			sprintf(portstr, "%x", gpio->port);
+		printf("%12d: %4s %12s %3d %s\n", i, portstr,
+		       gpio->polarity == CB_GPIO_ACTIVE_LOW ? "active-low" :
+		       "active-high", gpio->value, gpio->name);
+	}
+	print_dec("MACs", info->num_macs);
+	for (i = 0; i < info->num_macs; i++) {
+		const struct mac_address *mac = &info->macs[i];
+		int j;
+
+		printf("%12d: ", i);
+		for (j = 0; j < sizeof(mac->mac_addr); j++)
+			printf("%s%02x", j ? ":" : "", mac->mac_addr[j]);
+		printf("\n");
+	}
+	print_str(">Serial #", info->serialno);
+	print_ptr("Multiboot tab", info->mbtable);
+	print_ptr("CB header", info->header);
+	print_ptr("CB mainboard", info->mainboard);
+	if (info->mainboard) {
+		struct cb_mainboard *mb = info->mainboard;
+
+		print_idx(">vendor", mb->vendor_idx, mb->strings);
+		print_idx(">part_number", mb->part_number_idx, mb->strings);
+	}
+	print_ptr("vboot handoff", info->vboot_handoff);
+	print_hex(">size", info->vboot_handoff_size);
+	print_ptr(">vdat addr", info->vdat_addr);
+	print_hex(">size", info->vdat_size);
+
+	print_addr64("SMBIOS", info->smbios_start);
+	print_hex(">size", info->smbios_size);
+	print_hex("ROM MTRR", info->x86_rom_var_mtrr_index);
+
+	print_ptr("Tstamp table", info->tstamp_table);
+	if (verbose && info->tstamp_table) {
+		struct timestamp_table *ts = info->tstamp_table;
+
+		printf("%-12s", "Base_time");
+		print_grouped_ull(ts->base_time, 12);
+		printf("\n");
+		print_dec("Tick MHz", ts->tick_freq_mhz);
+		for (i = 0; i < ts->num_entries; i++) {
+			const struct timestamp_entry *tse;
+
+			tse = &ts->entries[i];
+			printf("   ");
+			print_grouped_ull(tse->entry_stamp, 12);
+			printf("  %s\n", timestamp_name(tse->entry_id));
+		}
+	}
+
+	print_ptr("CBmem cons", info->cbmem_cons);
+	if (info->cbmem_cons) {
+		struct cbmem_console *cons = info->cbmem_cons;
+		int i;
+
+		print_hex("Size", cons->size);
+		print_hex("Cursor", cons->cursor);
+		if (verbose) {
+			for (i = 0; i < cons->cursor; i++) {
+				int ch = cons->body[i];
+
+				putc(ch);
+
+				if (ch == '\n') {
+					/* check for ctrl-c to abort... */
+					if (ctrlc()) {
+						puts("Abort\n");
+						return;
+					}
+					printf("   ");
+				}
+			}
+			printf("\n");
+		}
+	}
+
+	print_ptr("MRC cache", info->mrc_cache);
+	print_ptr("ACPI GNVS", info->acpi_gnvs);
+	print_hex("Board ID", info->board_id);
+	print_hex("RAM code", info->ram_code);
+	print_ptr("WiFi calib", info->wifi_calibration);
+	print_addr64("Ramoops buff", info->ramoops_buffer);
+	print_hex(">size", info->ramoops_buffer_size);
+	print_hex("SF size", info->spi_flash.size);
+	print_hex("SF sector", info->spi_flash.sector_size);
+	print_hex("SF erase cmd", info->spi_flash.erase_cmd);
+
+	print_addr64("FMAP offset", info->fmap_offset);
+	print_addr64("CBFS offset", info->cbfs_offset);
+	print_addr64("CBFS size", info->cbfs_size);
+	print_addr64("Boot media size", info->boot_media_size);
+	print_addr64("MTC start", info->mtc_start);
+	print_hex("MTC size", info->mtc_size);
+
+	print_ptr("Chrome OS VPD", info->chromeos_vpd);
+}
+
+static int do_cbsysinfo(struct cmd_tbl *cmdtp, int flag, int argc,
+			char *const argv[])
+{
+	bool verbose = false;
+
+	if (argc > 1) {
+		if (!strcmp("-v", argv[1]))
+			verbose = true;
+		else
+			return CMD_RET_USAGE;
+	}
+
+	if (!gd->arch.coreboot_table) {
+		printf("No coreboot sysinfo table found\n");
+		return CMD_RET_FAILURE;
+	}
+	show_table(&lib_sysinfo, verbose);
+
+	return 0;
+}
+
+U_BOOT_CMD(
+	cbsysinfo,	2,	1,	do_cbsysinfo,
+	"Show coreboot sysinfo table",
+	"[-v]         Dumps out the contents of the sysinfo table. This only\n"
+	"works if U-Boot is booted from coreboot"
+);
diff --git a/common/board_info.c b/common/board_info.c
index b54aa30..1cfe34f 100644
--- a/common/board_info.c
+++ b/common/board_info.c
@@ -1,31 +1,52 @@
 // SPDX-License-Identifier: GPL-2.0+
 
 #include <common.h>
+#include <dm.h>
 #include <init.h>
+#include <sysinfo.h>
 #include <asm/global_data.h>
 #include <linux/libfdt.h>
 #include <linux/compiler.h>
 
+DECLARE_GLOBAL_DATA_PTR;
+
 int __weak checkboard(void)
 {
 	return 0;
 }
 
 /*
- * If the root node of the DTB has a "model" property, show it.
+ * Check sysinfo for board information. Failing that if the root node of the DTB
+ * has a "model" property, show it.
+ *
  * Then call checkboard().
  */
 int __weak show_board_info(void)
 {
-#ifdef CONFIG_OF_CONTROL
-	DECLARE_GLOBAL_DATA_PTR;
-	const char *model;
+	if (IS_ENABLED(CONFIG_OF_CONTROL)) {
+		struct udevice *dev;
+		const char *model;
+		char str[80];
+		int ret = -ENOSYS;
 
-	model = fdt_getprop(gd->fdt_blob, 0, "model", NULL);
+		if (IS_ENABLED(CONFIG_SYSINFO)) {
+			/* This might provide more detail */
+			ret = uclass_first_device_err(UCLASS_SYSINFO, &dev);
+			if (!ret)
+				ret = sysinfo_get_str(dev,
+						      SYSINFO_ID_BOARD_MODEL,
+						      sizeof(str), str);
+		}
+
+		/* Fail back to the main 'model' if available */
+		if (ret)
+			model = fdt_getprop(gd->fdt_blob, 0, "model", NULL);
+		else
+			model = str;
 
-	if (model)
-		printf("Model: %s\n", model);
-#endif
+		if (model)
+			printf("Model: %s\n", model);
+	}
 
 	return checkboard();
 }
diff --git a/common/bootm.c b/common/bootm.c
index dab7c36..ea71522 100644
--- a/common/bootm.c
+++ b/common/bootm.c
@@ -583,7 +583,8 @@
 		if (ret)
 			return log_msg_ret("silent", ret);
 	}
-	if (IS_ENABLED(CONFIG_BOOTARGS_SUBST) && (flags & BOOTM_CL_SUBST)) {
+	if (IS_ENABLED(CONFIG_BOOTARGS_SUBST) && IS_ENABLED(CONFIG_CMDLINE) &&
+	    (flags & BOOTM_CL_SUBST)) {
 		ret = process_subst(buf, maxlen);
 		if (ret)
 			return log_msg_ret("subst", ret);
diff --git a/common/bootstage.c b/common/bootstage.c
index 2c0110c..4621105 100644
--- a/common/bootstage.c
+++ b/common/bootstage.c
@@ -9,6 +9,8 @@
  * permits accurate timestamping of each.
  */
 
+#define LOG_CATEGORY	LOGC_BOOT
+
 #include <common.h>
 #include <bootstage.h>
 #include <hang.h>
@@ -127,12 +129,16 @@
 
 	/* Only record the first event for each */
 	rec = find_id(data, id);
-	if (!rec && data->rec_count < RECORD_COUNT) {
-		rec = &data->record[data->rec_count++];
-		rec->time_us = mark;
-		rec->name = name;
-		rec->flags = flags;
-		rec->id = id;
+	if (!rec) {
+		if (data->rec_count < RECORD_COUNT) {
+			rec = &data->record[data->rec_count++];
+			rec->time_us = mark;
+			rec->name = name;
+			rec->flags = flags;
+			rec->id = id;
+		} else {
+			log_warning("Bootstage space exhasuted\n");
+		}
 	}
 
 	/* Tell the board about this progress */
diff --git a/common/image-fit.c b/common/image-fit.c
index 28b3d2b..2d0ece6 100644
--- a/common/image-fit.c
+++ b/common/image-fit.c
@@ -1512,6 +1512,10 @@
 	uint8_t image_arch;
 	int aarch32_support = 0;
 
+	/* Let's assume that sandbox can load any architecture */
+	if (IS_ENABLED(CONFIG_SANDBOX))
+		return true;
+
 	if (IS_ENABLED(CONFIG_ARM64_SUPPORT_AARCH32))
 		aarch32_support = 1;
 
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index 584f848..0711cbf 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -500,7 +500,7 @@
 	  future requests for that data can be served faster. Enable this option
 	  to build the drivers in drivers/cache as part of an SPL build.
 
-config SPL_CPU_SUPPORT
+config SPL_CPU
 	bool "Support CPU drivers"
 	help
 	  Enable this to support CPU drivers in SPL. These drivers can set
diff --git a/common/spl/spl.c b/common/spl/spl.c
index 5f51098..556a91a 100644
--- a/common/spl/spl.c
+++ b/common/spl/spl.c
@@ -387,6 +387,22 @@
 
 #endif /* HANDOFF */
 
+/**
+ * get_bootstage_id() - Get the bootstage ID to emit
+ *
+ * @start: true if this is for starting SPL, false for ending it
+ * @return bootstage ID to use
+ */
+static enum bootstage_id get_bootstage_id(bool start)
+{
+	enum u_boot_phase phase = spl_phase();
+
+	if (IS_ENABLED(CONFIG_TPL_BUILD) && phase == PHASE_TPL)
+		return start ? BOOTSTAGE_ID_START_TPL : BOOTSTAGE_ID_END_TPL;
+	else
+		return start ? BOOTSTAGE_ID_START_SPL : BOOTSTAGE_ID_END_SPL;
+}
+
 static int spl_common_init(bool setup_malloc)
 {
 	int ret;
@@ -417,8 +433,8 @@
 			      __func__, ret);
 	}
 #endif /* CONFIG_BOOTSTAGE_STASH */
-	bootstage_mark_name(spl_phase() == PHASE_TPL ? BOOTSTAGE_ID_START_TPL :
-			    BOOTSTAGE_ID_START_SPL, SPL_TPL_NAME);
+	bootstage_mark_name(get_bootstage_id(true),
+			    spl_phase_name(spl_phase()));
 #if CONFIG_IS_ENABLED(LOG)
 	ret = log_init();
 	if (ret) {
@@ -733,8 +749,7 @@
 	debug("SPL malloc() used 0x%lx bytes (%ld KB)\n", gd->malloc_ptr,
 	      gd->malloc_ptr / 1024);
 #endif
-	bootstage_mark_name(spl_phase() == PHASE_TPL ? BOOTSTAGE_ID_END_TPL :
-			    BOOTSTAGE_ID_END_SPL, "end " SPL_TPL_NAME);
+	bootstage_mark_name(get_bootstage_id(false), "end phase");
 #ifdef CONFIG_BOOTSTAGE_STASH
 	ret = bootstage_stash((void *)CONFIG_BOOTSTAGE_STASH_ADDR,
 			      CONFIG_BOOTSTAGE_STASH_SIZE);
diff --git a/configs/chromebook_coral_defconfig b/configs/chromebook_coral_defconfig
index e3756cf..4823913 100644
--- a/configs/chromebook_coral_defconfig
+++ b/configs/chromebook_coral_defconfig
@@ -43,7 +43,7 @@
 CONFIG_HANDOFF=y
 CONFIG_TPL_SYS_MALLOC_SIMPLE=y
 CONFIG_SPL_SEPARATE_BSS=y
-CONFIG_SPL_CPU_SUPPORT=y
+CONFIG_SPL_CPU=y
 CONFIG_SPL_DM_SPI_FLASH=y
 CONFIG_SPL_PCI=y
 # CONFIG_SPL_SPI_FLASH_TINY is not set
diff --git a/configs/chromebook_link64_defconfig b/configs/chromebook_link64_defconfig
index 8f56ee3..872e33d 100644
--- a/configs/chromebook_link64_defconfig
+++ b/configs/chromebook_link64_defconfig
@@ -31,7 +31,7 @@
 CONFIG_LAST_STAGE_INIT=y
 CONFIG_MISC_INIT_R=y
 CONFIG_SPL_SYS_MALLOC_SIMPLE=y
-CONFIG_SPL_CPU_SUPPORT=y
+CONFIG_SPL_CPU=y
 CONFIG_SPL_ENV_SUPPORT=y
 CONFIG_SPL_I2C_SUPPORT=y
 CONFIG_SPL_DM_SPI_FLASH=y
diff --git a/configs/qemu-x86_64_defconfig b/configs/qemu-x86_64_defconfig
index 99a489c..4815d8a 100644
--- a/configs/qemu-x86_64_defconfig
+++ b/configs/qemu-x86_64_defconfig
@@ -31,7 +31,7 @@
 CONFIG_LAST_STAGE_INIT=y
 CONFIG_PCI_INIT_R=y
 CONFIG_SPL_SYS_MALLOC_SIMPLE=y
-CONFIG_SPL_CPU_SUPPORT=y
+CONFIG_SPL_CPU=y
 CONFIG_SPL_ENV_SUPPORT=y
 CONFIG_SPL_DM_SPI_FLASH=y
 CONFIG_SPL_NET_SUPPORT=y
diff --git a/doc/arch/sandbox.rst b/doc/arch/sandbox.rst
index 8b18a22..e052b6b 100644
--- a/doc/arch/sandbox.rst
+++ b/doc/arch/sandbox.rst
@@ -389,6 +389,8 @@
    =>host bind 0 ./disk.raw
    =>ls host 0:2
 
+The device can be marked removeable with 'host bind -r'.
+
 A disk image can be created using the following commands::
 
    $> truncate -s 1200M ./disk.raw
diff --git a/doc/board/google/chromebook_coral.rst b/doc/board/google/chromebook_coral.rst
index c39f1e3..4b58567 100644
--- a/doc/board/google/chromebook_coral.rst
+++ b/doc/board/google/chromebook_coral.rst
@@ -16,6 +16,169 @@
 Slim Bootloader. This documentation refers to a 'bare metal' port.
 
 
+Building
+--------
+
+First, you need the following binary blobs:
+
+   * descriptor.bin - Intel flash descriptor
+   * fitimage.bin - Base flash image structure
+   * fsp_m.bin - FSP-M, for setting up SDRAM
+   * fsp_s.bin - FSP-S, for setting up Silicon
+   * vbt.bin - for setting up display
+
+These binaries do not seem to be available publicly. If you have a ROM image,
+such as santa.bin then you can do this::
+
+  cbfstool santa.bin extract -n fspm.bin -f fsp-m.bin
+  cbfstool santa.bin extract -n fsps.bin -f fsp-s.bin
+  cbfstool santa.bin extract -n vbt-santa.bin -f vbt.bin
+  mkdir tmp
+  cd tmp
+  dump_fmap -x ../santa.bin
+  mv SI_DESC ../descriptor.bin
+  mv IFWI ../fitimage.bin
+
+Put all of these files in `board/google/chromebook_coral` so they can be found
+by the build.
+
+To build::
+
+  make O=/tmp/b/chromebook_coral chromebook_coral_defconfig
+  make O=/tmp/b/chromebook_coral -s -j30 all
+
+That should produce `/tmp/b/chrombook_coral/u-boot.rom` which you can use with
+a Dediprog em100::
+
+  em100 -s -c w25q128fw -d /tmp/b/chromebook_coral/u-boot.rom -r
+
+or you can use flashrom to write it to the board. If you do that, make sure you
+have a way to restore the old ROM without booting the board. Otherwise you may
+brick it. Having said that, you may find these instructions useful if you want
+to unbrick your device:
+
+  https://chromium.googlesource.com/chromiumos/platform/ec/+/cr50_stab/docs/case_closed_debugging.md
+
+You can buy Suzy-Q from Sparkfun:
+
+  https://chromium.googlesource.com/chromiumos/third_party/hdctools/+/main/docs/ccd.md#suzyq-suzyqable
+
+Note that it will hang at the SPL prompt for 21 seconds. When booting into
+Chrome OS it will always select developer mode, so will wipe anything you have
+on the device if you let it proceed. You have two seconds in U-Boot to stop the
+auto-boot prompt and several seconds at the 'developer wipe' screen to stop it
+wiping the disk.
+
+Here is the console output::
+
+  U-Boot TPL 2021.04-rc1-00128-g344eefcdfec-dirty (Feb 11 2021 - 20:13:08 -0700)
+  Trying to boot from Mapped SPI
+
+  U-Boot SPL 2021.04-rc1-00128-g344eefcdfec-dirty (Feb 11 2021 - 20:13:08 -0700)
+  Trying to boot from Mapped SPI
+
+
+  U-Boot 2021.04-rc1-00128-g344eefcdfec-dirty (Feb 11 2021 - 20:13:08 -0700)
+
+  CPU:   Intel(R) Celeron(R) CPU N3450 @ 1.10GHz
+  DRAM:  3.9 GiB
+  MMC:   sdmmc@1b,0: 1, emmc@1c,0: 2
+  Video: 1024x768x32 @ b0000000
+  Model: Google Coral
+  Net:   No ethernet found.
+  SF: Detected w25q128fw with page size 256 Bytes, erase size 4 KiB, total 16 MiB
+  Hit any key to stop autoboot:  0
+  cmdline=console= loglevel=7 init=/sbin/init cros_secure oops=panic panic=-1 root=PARTUUID=${uuid}/PARTNROFF=1 rootwait rw dm_verity.error_behavior=3 dm_verity.max_bios=-1 dm_verity.dev_wait=0 dm="1 vroot none rw 1,0 3788800 verity payload=ROOT_DEV hashtree=HASH_DEV hashstart=3788800 alg=sha1 root_hexdigest=55052b629d3ac889f25a9583ea12cdcd3ea15ff8 salt=a2d4d9e574069f4fed5e3961b99054b7a4905414b60a25d89974a7334021165c" noinitrd vt.global_cursor_default=0 kern_guid=${uuid} add_efi_memmap boot=local noresume noswap i915.modeset=1 Kernel command line: "console= loglevel=7 init=/sbin/init cros_secure oops=panic panic=-1 root=PARTUUID=35c775e7-3735-d745-93e5-d9e0238f7ed0/PARTNROFF=1 rootwait rw dm_verity.error_behavior=3 dm_verity.max_bios=-1 dm_verity.dev_wait=0 dm="1 vroot none rw 1,0 3788800 verity payload=ROOT_DEV hashtree=HASH_DEV hashstart=3788800 alg=sha1 root_hexdigest=55052b629d3ac889f25a9583ea12cdcd3ea15ff8 salt=a2d4d9e574069f4fed5e3961b99054b7a4905414b60a25d89974a7334021165c" noinitrd vt.global_cursor_default=0 kern_guid=35c775e7-3735-d745-93e5-d9e0238f7ed0 add_efi_memmap boot=local noresume noswap i915.modeset=1 tpm_tis.force=1 tpm_tis.interrupts=0 nmi_watchdog=panic,lapic disablevmx=off  "
+  Setup located at 00090000:
+
+  ACPI RSDP addr      : 7991f000
+  E820: 14 entries
+                Addr              Size  Type
+      d0000000     1000000  <NULL>
+             0       a0000  RAM
+         a0000       60000  Reserved
+      7b000000      800000  Reserved
+      7b800000     4800000  Reserved
+      7ac00000      400000  Reserved
+        100000     ff00000  RAM
+      10000000     2151000  Reserved
+      12151000    68aaf000  RAM
+     100000000    80000000  RAM
+      e0000000    10000000  Reserved
+      7991bfd0     12e4030  Reserved
+      d0000000    10000000  Reserved
+      fed10000        8000  Reserved
+  Setup sectors       : 1e
+  Root flags          : 1
+  Sys size            : 63420
+  RAM size            : 0
+  Video mode          : ffff
+  Root dev            : 0
+  Boot flag           : 0
+  Jump                : 66eb
+  Header              : 53726448
+                        Kernel V2
+  Version             : 20d
+  Real mode switch    : 0
+  Start sys           : 1000
+  Kernel version      : 38cc
+     @00003acc:
+  Type of loader      : 80
+                        U-Boot, version 0
+  Load flags          : 81
+                      : loaded-high can-use-heap
+  Setup move size     : 8000
+  Code32 start        : 100000
+  Ramdisk image       : 0
+  Ramdisk size        : 0
+  Bootsect kludge     : 0
+  Heap end ptr        : 8e00
+  Ext loader ver      : 0
+  Ext loader type     : 0
+  Command line ptr    : 99000
+     console= loglevel=7 init=/sbin/init cros_secure oops=panic panic=-1 root=PARTUUID=35c775e7-3735-d745-93e5-d9e0238f7ed0/PARTNROFF=1 rootwait rw dm_verity.error_behavior=3 dm_verity.max_bios=-1 dm_verity.dev_wait=0 dm="1 vroot none rw 1,0 3788800 verity payload=ROOT_DEV hashtree=HASH_DEV hashstart=3788800 alg=sha1 root_hexdigest=55052b629d3ac889f25a9583ea12cdcd3ea15ff8 salt=a2d4d9e574069f4fed5e3961b99054b7a4905414b60a25d89974a7334021165c" noinitrd vt.global_cursor_default=0 kern_guid=35c775e7-3735-d745-93e5-d9e0238f7ed0 add_efi_memmap boot=local noresume noswap i915.modeset=1 tpm_tis.force=1 tpm_tis.interrupts=0 nmi_watchdog=panic,lapic disablevmx=off
+  Initrd addr max     : 7fffffff
+  Kernel alignment    : 200000
+  Relocatable kernel  : 1
+  Min alignment       : 15
+                      : 200000
+  Xload flags         : 3
+                      : 64-bit-entry can-load-above-4gb
+  Cmdline size        : 7ff
+  Hardware subarch    : 0
+  HW subarch data     : 0
+  Payload offset      : 26e
+  Payload length      : 612045
+  Setup data          : 0
+  Pref address        : 1000000
+  Init size           : 1383000
+  Handover offset     : 0
+
+  Starting kernel ...
+
+  Timer summary in microseconds (17 records):
+         Mark    Elapsed  Stage
+            0          0  reset
+      155,279    155,279  TPL
+      237,088     81,809  end phase
+      237,533        445  SPL
+      816,456    578,923  end phase
+      817,357        901  board_init_f
+    1,061,751    244,394  board_init_r
+    1,402,435    340,684  id=64
+    1,430,071     27,636  main_loop
+    5,532,057  4,101,986  start_kernel
+
+  Accumulated time:
+                     685  dm_r
+                   2,817  fast_spi
+                  33,095  dm_spl
+                  52,468  dm_f
+                 208,242  fsp-m
+                 242,221  fsp-s
+                 332,710  mmap_spi
+
+
 Boot flow - TPL
 ---------------
 
@@ -181,7 +344,7 @@
     ff000000       Bottom of ROM
     fefc0000       Top of CAR region
     fef96000       Stack for FSP-M
-    fef40000 59000 FSP-M
+    fef40000 59000 FSP-M (also VPL loads here)
     fef11000       SPL loaded here
     fef10000       CONFIG_BLOBLIST_ADDR
     fef10000       Stack top in TPL, SPL and U-Boot before relocation
@@ -194,36 +357,73 @@
       200000       FSP-S (which is run after U-Boot is relocated)
      1110000       CONFIG_SYS_TEXT_BASE
 
+
+Speeding up SPL for development
+-------------------------------
+
+The 21-second wait for memory training is annoying during development, since
+every new image incurs this cost when booting. There is no cache to fall back on
+since that area of the image is empty on start-up.
+
+You can add suitable cache contents to the image to fix this, for development
+purposes only, like this::
+
+   # Read the image back after booting through SPL
+   em100 -s -c w25q128fw -u image.bin
+
+   # Extract the two cache regions
+   binman extract -i image.bin extra *cache
+
+   # Move them into the source directory
+   mv *cache board/google/chromebook_coral
+
+Then add something like this to the devicetree::
+
+  #if IS_ENABLED(CONFIG_HAVE_MRC) || IS_ENABLED(CONFIG_FSP_VERSION2)
+     /* Provide initial contents of the MRC data for faster development */
+     rw-mrc-cache {
+        type = "blob";
+        /* Mirror the offset in spi-flash@0 */
+        offset = <0xff8e0000>;
+        size = <0x10000>;
+        filename = "board/google/chromebook_coral/rw-mrc-cache";
+     };
+     rw-var-mrc-cache {
+        type = "blob";
+        size = <0x1000>;
+        filename = "board/google/chromebook_coral/rw-var-mrc-cache";
+     };
+  #endif
+
+This tells binman to put the cache contents in the same place as the
+`rw-mrc-cache` and `rw-var-mrc-cache` regions defined by the SPI-flash driver.
+
 
 Supported peripherals
 ---------------------
 
-- UART
-- SPI flash
-- Video
-- MMC (dev 0) and micro-SD (dev 1)
-- Chrome OS EC
-- Keyboard
-- USB
+The following have U-Boot drivers:
 
+   - UART
+   - SPI flash
+   - Video
+   - MMC (dev 0) and micro-SD (dev 1)
+   - Chrome OS EC
+   - Cr50 (security chip)
+   - Keyboard
+   - USB
+
 
 To do
 -----
 
 - Finish peripherals
-   - left-side USB
-   - USB-C
-   - Cr50 (security chip: a basic driver is running but not included here)
    - Sound (Intel I2S support exists, but need da7219 driver)
-   - Various minor features supported by LPC, etc.
-- Booting Chrome OS, e.g. with verified boot
-- Integrate with Chrome OS vboot
-- Improvements to booting from coreboot (i.e. as a coreboot target)
 - Use FSP-T binary instead of our own CAR implementation
 - Use the official FSP package instead of the coreboot one
-- Enable all CPU cores
 - Suspend / resume
-- ACPI
+- Fix MMC which seems to try to read even though the card is empty
+- Fix USB3 crash "WARN halted endpoint, queueing URB anyway."
 
 
 Credits
diff --git a/doc/README.chromium-chainload b/doc/chromium/chainload.rst
similarity index 78%
rename from doc/README.chromium-chainload
rename to doc/chromium/chainload.rst
index 45eaece..7b6bb10 100644
--- a/doc/README.chromium-chainload
+++ b/doc/chromium/chainload.rst
@@ -1,3 +1,6 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright 2020 Google LLC
+
 Running U-Boot from coreboot on Chromebooks
 ===========================================
 
@@ -15,7 +18,7 @@
 
 
 For all of these the standard U-Boot build instructions apply. For example on
-ARM:
+ARM::
 
    sudo apt install gcc-arm-linux-gnueabi
    mkdir b
@@ -37,14 +40,17 @@
 Nyan-big
 --------
 
-Compiled based on information here:
-https://lists.denx.de/pipermail/u-boot/2015-March/209530.html
-https://git.collabora.com/cgit/user/tomeu/u-boot.git/commit/?h=nyan-big
-https://lists.denx.de/pipermail/u-boot/2017-May/289491.html
-https://github.com/chromeos-nvidia-androidtv/gnu-linux-on-acer-chromebook-13#copy-data-to-the-sd-card
+Compiled based on information here::
+
+   https://lists.denx.de/pipermail/u-boot/2015-March/209530.html
+   https://git.collabora.com/cgit/user/tomeu/u-boot.git/commit/?h=nyan-big
+   https://lists.denx.de/pipermail/u-boot/2017-May/289491.html
+   https://github.com/chromeos-nvidia-androidtv/gnu-linux-on-acer-chromebook-13#copy-data-to-the-sd-card
 
 1. Build U-Boot
 
+Steps::
+
    mkdir b
    make -j8 O=b/nyan-big CROSS_COMPILE=arm-linux-gnueabi- nyan-big_defconfig all
 
@@ -61,16 +67,21 @@
 
 3. Build and sign an image
 
-   ./b/nyan-big/tools/mkimage -f doc/chromium/nyan-big.its u-boot-chromium.fit
+Steps::
+
+   ./b/nyan-big/tools/mkimage -f doc/chromium/files/nyan-big.its u-boot-chromium.fit
    echo test >dummy.txt
-   vbutil_kernel --arch arm --keyblock doc/chromium/devkeys/kernel.keyblock \
-	--signprivate doc/chromium/devkeys/kernel_data_key.vbprivk \
-	--version 1 --config dummy.txt --vmlinuz u-boot-chromium.fit \
-	--bootloader dummy.txt --pack u-boot.kpart
+   vbutil_kernel --arch arm \
+     --keyblock doc/chromium/files/devkeys/kernel.keyblock \
+     --signprivate doc/chromium/files/devkeys/kernel_data_key.vbprivk \
+     --version 1 --config dummy.txt --vmlinuz u-boot-chromium.fit \
+     --bootloader dummy.txt --pack u-boot.kpart
 
 
 4. Prepare an SD card
 
+Steps::
+
    DISK=/dev/sdc   # Replace with your actual SD card device
    sudo cgpt create $DISK
    sudo cgpt add -b 34 -s 32768 -P 1 -S 1 -t kernel $DISK
@@ -80,6 +91,8 @@
 
 5. Write U-Boot to the SD card
 
+Steps::
+
    sudo dd if=u-boot.kpart of=/dev/sdc1; sync
 
 
@@ -90,7 +103,7 @@
 'enable_dev_usb_boot'. You only need to do this once.
 
 Reboot the device with the SD card inserted. Press Clrl-U at the developer
-mode screen. It should show something like the following on the display:
+mode screen. It should show something like the following on the display::
 
    U-Boot 2017.07-00637-g242eb42-dirty (May 22 2017 - 06:14:21 -0600)
 
@@ -104,9 +117,9 @@
 
 7. Known problems
 
-On the serial console the word MMC is chopped at the start of the line:
+On the serial console the word MMC is chopped at the start of the line::
 
-C:   sdhci@700b0000: 2, sdhci@700b0400: 1, sdhci@700b0600: 0
+   C:   sdhci@700b0000: 2, sdhci@700b0400: 1, sdhci@700b0600: 0
 
 This is likely due to some problem with change-over of the serial driver
 during relocation (or perhaps updating the clock setup in board_init()).
@@ -116,7 +129,7 @@
 
 To check that you copied the u-boot.its file correctly, use these commands.
 You should see that the data at 0x100 in u-boot-chromium.fit is the first few
-bytes of U-Boot:
+bytes of U-Boot::
 
    hd u-boot-chromium.fit |head -20
    ...
@@ -141,34 +154,39 @@
 
 Open include/configs/rk3288_common.h
 
-Change:
+Change::
 
-#define CONFIG_SYS_TEXT_BASE		0x00100000
+   #define CONFIG_SYS_TEXT_BASE		0x00100000
 
-to:
+to::
 
-#define CONFIG_SYS_TEXT_BASE		0x02000100
+   #define CONFIG_SYS_TEXT_BASE		0x02000100
 
 
 
 2. Build U-Boot
 
+Steps::
+
    mkdir b
    make -j8 O=b/chromebook_jerry CROSS_COMPILE=arm-linux-gnueabi- \
-	chromebook_jerry_defconfig all
+      chromebook_jerry_defconfig all
 
 
 3. See above
 
 4. Build and sign an image
 
+Steps::
+
    ./b/chromebook_jerry/tools/mkimage -f doc/chromium/chromebook_jerry.its \
-	u-boot-chromium.fit
+      u-boot-chromium.fit
    echo test >dummy.txt
-   vbutil_kernel --arch arm --keyblock doc/chromium/devkeys/kernel.keyblock \
-	--signprivate doc/chromium/devkeys/kernel_data_key.vbprivk \
-	--version 1 --config dummy.txt --vmlinuz u-boot-chromium.fit \
-	--bootloader dummy.txt --pack u-boot.kpart
+   vbutil_kernel --arch arm \
+      --keyblock doc/chromium/files/devkeys/kernel.keyblock \
+      --signprivate doc/chromium/files/devkeys/kernel_data_key.vbprivk \
+      --version 1 --config dummy.txt --vmlinuz u-boot-chromium.fit \
+      --bootloader dummy.txt --pack u-boot.kpart
 
 
 5. See above
@@ -182,7 +200,7 @@
 'enable_dev_usb_boot'. You only need to do this once.
 
 Reboot the device with the SD card inserted. Press Clrl-U at the developer
-mode screen. It should show something like the following on the display:
+mode screen. It should show something like the following on the display::
 
    U-Boot 2017.05-00649-g72acdbf-dirty (May 29 2017 - 14:57:05 -0600)
 
@@ -203,18 +221,18 @@
 
 
 Other notes
-===========
+-----------
 
 flashrom
---------
+~~~~~~~~
 
-   Used to make a backup of your firmware, or to replace it.
+Used to make a backup of your firmware, or to replace it.
 
-   See: https://www.chromium.org/chromium-os/packages/cros-flashrom
+See: https://www.chromium.org/chromium-os/packages/cros-flashrom
 
 
 coreboot
---------
+~~~~~~~~
 
 Coreboot itself is not designed to actually boot an OS. Instead, a program
 called Depthcharge is used. This originally came out of U-Boot and was then
diff --git a/doc/chromium/chromebook_jerry.its b/doc/chromium/files/chromebook_jerry.its
similarity index 100%
rename from doc/chromium/chromebook_jerry.its
rename to doc/chromium/files/chromebook_jerry.its
diff --git a/doc/chromium/devkeys/kernel.keyblock b/doc/chromium/files/devkeys/kernel.keyblock
similarity index 100%
rename from doc/chromium/devkeys/kernel.keyblock
rename to doc/chromium/files/devkeys/kernel.keyblock
Binary files differ
diff --git a/doc/chromium/devkeys/kernel_data_key.vbprivk b/doc/chromium/files/devkeys/kernel_data_key.vbprivk
similarity index 100%
rename from doc/chromium/devkeys/kernel_data_key.vbprivk
rename to doc/chromium/files/devkeys/kernel_data_key.vbprivk
Binary files differ
diff --git a/doc/chromium/nyan-big.its b/doc/chromium/files/nyan-big.its
similarity index 100%
rename from doc/chromium/nyan-big.its
rename to doc/chromium/files/nyan-big.its
diff --git a/doc/chromium/index.rst b/doc/chromium/index.rst
new file mode 100644
index 0000000..0722c25
--- /dev/null
+++ b/doc/chromium/index.rst
@@ -0,0 +1,14 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright 2020 Google LLC
+
+Chromium OS-specific doc
+========================
+
+This provides some information about Chromium OS and U-Boot.
+
+.. toctree::
+   :maxdepth: 2
+
+   overview
+   run_vboot
+   chainload
diff --git a/doc/chromium/overview.rst b/doc/chromium/overview.rst
new file mode 100644
index 0000000..5498ed9
--- /dev/null
+++ b/doc/chromium/overview.rst
@@ -0,0 +1,74 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright 2020 Google LLC
+
+Chromium OS Support in U-Boot
+=============================
+
+Introduction
+------------
+
+This describes how to use U-Boot with Chromium OS. Several options are
+available:
+
+   - Running U-Boot from the 'altfw' feature, which is available on selected
+     Chromebooks from 2019 onwards (initially Grunt). Press '1' from the
+     developer-mode screen to get into U-Boot. See here for details:
+     https://chromium.googlesource.com/chromiumos/docs/+/HEAD/developer_mode.md
+
+   - Running U-Boot from the disk partition. This involves signing U-Boot and
+     placing it on the disk, for booting as a 'kernel'. See
+     :doc:`chainload` for information on this. This is the only
+     option on non-U-Boot Chromebooks from 2013 to 2018 and is somewhat
+     more involved.
+
+   - Running U-Boot with Chromium OS verified boot. This allows U-Boot to be
+     used instead of either or both of depthcharge (a bootloader which forked
+     from U-Boot in 2013) and coreboot. See :doc:`run_vboot` for more
+     information on this.
+
+   - Running U-Boot from coreboot. This allows U-Boot to run on more devices
+     since many of them only support coreboot as the bootloader and have
+     no bare-metal support in U-Boot. For this, use the 'coreboot' target.
+
+   - Running U-Boot and booting into a Chrome OS image, but without verified
+     boot. This can be useful for testing.
+
+
+Talks and documents
+-------------------
+
+Here is some material relevant to Chromium OS verified boot with U-Boot:
+
+   - "U-Boot with Chrome OS and firmware packaging"
+
+     - Author: Simon Glass
+     - Presented at Open Source Firmware Conference 2018, Erlangen
+     - Describes the work in progress as at the end of 2018
+     - Slides at `OSFC <https://2018.osfc.io/uploads/talk/paper/26/U-Boot_with_Chrome_OS_and_firmware_packaging.pdf>`_
+     - Video on `Youtube <https://www.youtube.com/watch?v=1jknxUvmwpo>`_
+
+   - "Verified Boot in Chrome OS and how to make it work for you"
+
+     - Author: Simon Glass
+     - Presented at ELCE 2013, Edinburgh
+     - Describes the original 2013 implementation as shipped on snow (first
+       `ARM Chromebook was a Samsung Chromebook <https://www.cnet.com/products/samsung-series-3-chromebook-xe303c12-11-6-exynos-5250-2-gb-ram-16-gb-ssd-bilingual-english-french/>`_
+       with Samsung Exynos5250 `review <https://www.cnet.com/reviews/samsung-chromebook-series-3-review/>`_),
+       spring (`HP Chromebook 11 <https://www.cnet.com/products/hp-chromebook-11-g2-11-6-exynos-5250-4-gb-ram-16-gb-emmc/>`_)
+       and pit/pi (`Samsung Chromebook 2 <https://www.cnet.com/products/samsung-chromebook-2-xe503c12-11-6-exynos-5-octa-4-gb-ram-16-gb-ssd/>`_
+       with Exynos 5 Octa 5420 in 2014).
+     - Slides at `Google research <https://research.google/pubs/pub42038/>`_
+     - Video at `Youtube <https://www.youtube.com/watch?v=kdpZC9jFzZA>`_
+
+   - "Chrome University 2018: Chrome OS Firmware and Verified Boot 201"
+
+     - Author: Duncan Laurie
+     - Describes Chrome OS firmware as of 2018 and includes a wide range of
+       topics. This has no U-Boot information, but does cover coreboot and also
+       talks about the Chrome OS EC and Security chip. This is probably the
+       best introduction talk.
+     - Video at `YouTube <https://www.youtube.com/watch?v=WY2sWpuda2g>`_
+
+   - `Chromium OS U-Boot <https://www.chromium.org/developers/u-boot>`_
+
+   - `Firmware porting Guide <https://www.chromium.org/chromium-os/firmware-porting-guide>`_
diff --git a/doc/README.chromium b/doc/chromium/run_vboot.rst
similarity index 67%
rename from doc/README.chromium
rename to doc/chromium/run_vboot.rst
index 75f2f24..41b4f63 100644
--- a/doc/README.chromium
+++ b/doc/chromium/run_vboot.rst
@@ -1,42 +1,14 @@
-Chromium OS Support in U-Boot
-=============================
-
-Introduction
-------------
-
-This describes how to use U-Boot with Chromium OS. Several options are
-available:
-
-   - Running U-Boot from the 'altfw' feature, which is available on selected
-        Chromebooks from 2019 onwards (initially Grunt). Press '1' from the
-        developer-mode screen to get into U-Boot. See here for details:
-        https://sites.google.com/a/chromium.org/dev/chromium-os/poking-around-your-chrome-os-device?pli=1
-
-   - Running U-Boot from the disk partition. This involves signing U-Boot and
-        placing it on the disk, for booting as a 'kernel'. See
-        README.chromium-chainload for information on this. This is the only
-        option on non-U-Boot Chromebooks from 2013 to 2018 and is somewhat
-        more involved.
-
-   - Running U-Boot with Chromium OS verified boot. This allows U-Boot to be
-        used instead of either or both of depthcharge (a bootloader which forked
-        from U-Boot in 2013) and coreboot. See below for more information on
-        this.
-
-   - Running U-Boot from coreboot. This allows U-Boot to run on more devices
-        since many of them only support coreboot as the bootloader and have
-        no bare-metal support in U-Boot. For this, use the 'coreboot' target.
-
-   - Running U-Boot and booting into a Chrome OS image, but without verified
-        boot. This can be useful for testing.
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright 2020 Google LLC
+.. sectionauthor:: Simon Glass <sjg@chromium.org>
 
 
-U-Boot with Chromium OS verified boot
--------------------------------------
+Running U-Boot with Chromium OS verified boot
+=============================================
 
-To obtain:
+To obtain::
 
-   git clone https://github.com/sglass68/u-boot.git
+   git clone https://github.com/sjg20/u-boot.git
    cd u-boot
    git checkout cros-master
 
@@ -46,29 +18,36 @@
    git checkout 45964294
    #  futility: updater: Correct output version for Snow
 
-To build for sandbox:
+To build for sandbox::
 
    UB=/tmp/b/chromeos_sandbox    # U-Boot build directory
    cd u-boot
    make O=$UB chromeos_sandbox_defconfig
    make O=$UB -j20 -s VBOOT_SOURCE=/path/to/vboot_reference \
-	MAKEFLAGS_VBOOT=DEBUG=1 QUIET=1
+     MAKEFLAGS_VBOOT=DEBUG=1 QUIET=1
 
 Replace sandbox with another supported target.
 
 This produces $UB/image.bin which contains the firmware binaries in a SPI
 flash image.
 
-To run on sandbox:
+To run on sandbox::
 
+   CROS=~/cosarm
+   IMG=$CROS/src/build/images/coral/latest/chromiumos_image.bin
    $UB/tpl/u-boot-tpl -d $UB/u-boot.dtb.out \
-	-L6 -c "host bind 0 $CROS/src/build/images/cheza/latest/chromiumos_image.bin; vboot go auto" \
-	-l -w -s state.dtb -r
+     -L6 -c "host bind 0 $IMG; vboot go auto" \
+     -l -w -s state.dtb -r -n -m $UB/ram
+
+   $UB/tpl/u-boot-tpl -d $UB/u-boot.dtb.out -L6 -l \
+     -c "host bind 0 $IMG; vboot go auto" -w -s $UB/state.dtb -r -n -m $UB/mem
+
 
 To run on other boards:
-   Install image.bin in the SPI flash of your device
-   Boot your system
 
+   - Install image.bin in the SPI flash of your device
+   - Boot your system
+
 
 Sandbox
 -------
@@ -83,7 +62,7 @@
 phases into state.dtb and will automatically ensure that memory is shared
 between all phases. TPL will jump to SPL and then on to U-Boot proper.
 
-It is possible to run with debugging on, e.g.
+It is possible to run with debugging on, e.g.::
 
    gdb --args $UB/tpl/u-boot-tpl -d ....
 
@@ -95,7 +74,7 @@
 -----
 
 Basic support is available for samus, using the chromeos_samus target. If you
-have an em100, use:
+have an em100, use::
 
    sudo em100 -s -c W25Q128FW -d $UB/image.bin -t -r
 
@@ -119,11 +98,20 @@
 
 Several uclasses are provided in cros/:
 
+UCLASS_CROS_AUX_FW
+   Chrome OS auxiliary firmware
+
-	UCLASS_CROS_AUX_FW		Chrome OS auxiliary firmware
-	UCLASS_CROS_FWSTORE		Chrome OS firmware storage
-	UCLASS_CROS_NVDATA		Chrome OS non-volatile data device
-	UCLASS_CROS_VBOOT_EC		Chrome OS vboot EC operations
-	UCLASS_CROS_VBOOT_FLAG		Chrome OS verified boot flag
+UCLASS_CROS_FWSTORE
+   Chrome OS firmware storage
+
+UCLASS_CROS_NVDATA
+   Chrome OS non-volatile data device
+
+UCLASS_CROS_VBOOT_EC
+   Chrome OS vboot EC operations
+
+UCLASS_CROS_VBOOT_FLAG
+   Chrome OS verified boot flag
 
 The existing UCLASS_CROS_EC is also used.
 
@@ -181,7 +169,7 @@
 U-Boot without Chromium OS verified boot
 ----------------------------------------
 
-The following script can be used to boot a Chrome OS image on coral:
+The following script can be used to boot a Chrome OS image on coral::
 
    # Read the image header and obtain the address of the kernel
    # The offset 4f0 is defined by verified boot and may change for other
@@ -213,6 +201,4 @@
 Get the full ACPI tables working with Coral
 
 
-Simon Glass
-sjg@chromium.org
 7 October 2018
diff --git a/doc/device-tree-bindings/sysinfo/google,coral.txt b/doc/device-tree-bindings/sysinfo/google,coral.txt
new file mode 100644
index 0000000..d8a1a79
--- /dev/null
+++ b/doc/device-tree-bindings/sysinfo/google,coral.txt
@@ -0,0 +1,37 @@
+Google Coral sysinfo information
+================================
+
+This binding allows information about the board to be described. It includes
+the SMBIOS binding as well.
+
+Required properties:
+
+  - compatible: "google,coral"
+  - recovery-gpios: GPIO to use for recovery button (-1 if none)
+  - wite-protect-gpios: GPIO to use for write-protect screw
+  - phase-enforce-gpios: GPIO to indicate the board is in final ship mode
+  - memconfig-gpios: 4 GPIOs to use to read memory config (as base2 int)
+
+Optional properties:
+  - skuconfig-gpios: 2 GPIOs to use to read SKU ID. If not present, the
+       Chromium OS EC SKU_ID is used instead
+
+Example:
+
+board: board {
+	compatible = "google,coral";
+	recovery-gpios = <&gpio_nw (-1) GPIO_ACTIVE_LOW>;
+	write-protect-gpios = <&gpio_nw GPIO_75 GPIO_ACTIVE_HIGH>;
+	phase-enforce-gpios = <&gpio_n GPIO_10 GPIO_ACTIVE_HIGH>;
+	memconfig-gpios = <&gpio_nw GPIO_101 GPIO_ACTIVE_HIGH
+		&gpio_nw GPIO_102 GPIO_ACTIVE_HIGH
+		&gpio_n GPIO_38 GPIO_ACTIVE_HIGH
+		&gpio_n GPIO_45 GPIO_ACTIVE_HIGH>;
+
+	/*
+	 * This is used for reef only:
+	 *
+	 * skuconfig-gpios = <&gpio_nw GPIO_16 GPIO_ACTIVE_HIGH
+	 *	&gpio_nw GPIO_17 GPIO_ACTIVE_HIGH>;
+	 */
+ };
diff --git a/doc/index.rst b/doc/index.rst
index f7aada9..02de1d4 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -87,6 +87,14 @@
 
    android/index
 
+Chromium OS-specific doc
+------------------------
+
+.. toctree::
+   :maxdepth: 2
+
+   chromium/index
+
 Indices and tables
 ==================
 
diff --git a/doc/usage/index.rst b/doc/usage/index.rst
index 35c515f..fb83434 100644
--- a/doc/usage/index.rst
+++ b/doc/usage/index.rst
@@ -21,6 +21,7 @@
    booti
    bootmenu
    button
+   x86/cbsysinfo
    conitrace
    echo
    exception
diff --git a/doc/usage/x86/cbsysinfo.rst b/doc/usage/x86/cbsysinfo.rst
new file mode 100644
index 0000000..8c03a85
--- /dev/null
+++ b/doc/usage/x86/cbsysinfo.rst
@@ -0,0 +1,25 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+cbsysinfo
+=========
+
+Synopis
+-------
+
+::
+
+    cbsysinfo
+
+
+Description
+-----------
+
+This displays information obtained from the coreboot sysinfo table. It is only
+useful when booting U-Boot from coreboot.
+
+Example
+-------
+
+::
+
+    => cbsysinfo
diff --git a/drivers/Makefile b/drivers/Makefile
index c562a71..3510dab 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -38,7 +38,7 @@
 
 obj-$(CONFIG_SPL_BOOTCOUNT_LIMIT) += bootcount/
 obj-$(CONFIG_SPL_CACHE_SUPPORT) += cache/
-obj-$(CONFIG_SPL_CPU_SUPPORT) += cpu/
+obj-$(CONFIG_SPL_CPU) += cpu/
 obj-$(CONFIG_SPL_CRYPTO_SUPPORT) += crypto/
 obj-$(CONFIG_SPL_MPC8XXX_INIT_DDR_SUPPORT) += ddr/fsl/
 obj-$(CONFIG_ARMADA_38X) += ddr/marvell/a38x/
diff --git a/drivers/block/sandbox.c b/drivers/block/sandbox.c
index e2f229b..1c2c3b4 100644
--- a/drivers/block/sandbox.c
+++ b/drivers/block/sandbox.c
@@ -89,7 +89,7 @@
 }
 
 #ifdef CONFIG_BLK
-int host_dev_bind(int devnum, char *filename)
+int host_dev_bind(int devnum, char *filename, bool removable)
 {
 	struct host_block_dev *host_dev;
 	struct udevice *dev;
@@ -146,7 +146,7 @@
 	}
 
 	desc = blk_get_devnum_by_type(IF_TYPE_HOST, devnum);
-	desc->removable = 1;
+	desc->removable = removable;
 	snprintf(desc->vendor, BLK_VEN_SIZE, "U-Boot");
 	snprintf(desc->product, BLK_PRD_SIZE, "hostfile");
 	snprintf(desc->revision, BLK_REV_SIZE, "1.0");
@@ -160,7 +160,7 @@
 	return ret;
 }
 #else
-int host_dev_bind(int dev, char *filename)
+int host_dev_bind(int dev, char *filename, bool removable)
 {
 	struct host_block_dev *host_dev = find_host_device(dev);
 
@@ -195,7 +195,7 @@
 	blk_dev->block_write = host_block_write;
 	blk_dev->devnum = dev;
 	blk_dev->part_type = PART_TYPE_UNKNOWN;
-	blk_dev->removable = 1;
+	blk_dev->removable = removable;
 	snprintf(blk_dev->vendor, BLK_VEN_SIZE, "U-Boot");
 	snprintf(blk_dev->product, BLK_PRD_SIZE, "hostfile");
 	snprintf(blk_dev->revision, BLK_REV_SIZE, "1.0");
diff --git a/drivers/gpio/intel_gpio.c b/drivers/gpio/intel_gpio.c
index ab46a94..f15ce7b 100644
--- a/drivers/gpio/intel_gpio.c
+++ b/drivers/gpio/intel_gpio.c
@@ -23,6 +23,7 @@
 #include <asm/pci.h>
 #include <asm/arch/gpio.h>
 #include <dm/acpi.h>
+#include <dm/device-internal.h>
 #include <dt-bindings/gpio/x86-gpio.h>
 
 static int intel_gpio_get_value(struct udevice *dev, uint offset)
@@ -85,7 +86,7 @@
 
 	/*
 	 * GPIO numbers are global in the device tree so it doesn't matter
-	 * which one is used
+	 * which @orig_dev is used
 	 */
 	gpio = args->args[0];
 	ret = intel_pinctrl_get_pad(gpio, &pinctrl, &desc->offset);
@@ -97,6 +98,17 @@
 	desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
 	desc->dev = dev;
 
+	/*
+	 * Handle the case where the wrong GPIO device was provided, since this
+	 * will not have been probed by the GPIO uclass before calling here
+	 * (see gpio_request_tail()).
+	 */
+	if (orig_dev != dev) {
+		ret = device_probe(dev);
+		if (ret)
+			return log_msg_ret("probe", ret);
+	}
+
 	return 0;
 }
 
diff --git a/drivers/misc/cbmem_console.c b/drivers/misc/cbmem_console.c
index 5ba0a54..8bbe33d 100644
--- a/drivers/misc/cbmem_console.c
+++ b/drivers/misc/cbmem_console.c
@@ -9,7 +9,7 @@
 #error This driver requires coreboot
 #endif
 
-#include <asm/arch/sysinfo.h>
+#include <asm/cb_sysinfo.h>
 
 struct cbmem_console {
 	u32 buffer_size;
diff --git a/drivers/misc/cros_ec_sandbox.c b/drivers/misc/cros_ec_sandbox.c
index cb8adc4..bc01df0 100644
--- a/drivers/misc/cros_ec_sandbox.c
+++ b/drivers/misc/cros_ec_sandbox.c
@@ -153,10 +153,14 @@
 {
 	struct ec_state *ec = g_state;
 
+	if (!g_state)
+		return 0;
+
 	/* We are guaranteed enough space to write basic properties */
 	fdt_setprop_u32(blob, node, "current-image", ec->current_image);
 	fdt_setprop(blob, node, "vbnv-context", ec->vbnv_context,
 		    sizeof(ec->vbnv_context));
+
 	return state_setprop(node, "flash-data", ec->flash_data,
 			     ec->ec_config.flash.length);
 }
diff --git a/drivers/misc/p2sb_emul.c b/drivers/misc/p2sb_emul.c
index 973d02d..51f8716 100644
--- a/drivers/misc/p2sb_emul.c
+++ b/drivers/misc/p2sb_emul.c
@@ -7,7 +7,6 @@
  */
 
 #define LOG_CATEGORY UCLASS_MISC
-#define LOG_DEBUG
 
 #include <common.h>
 #include <axi.h>
diff --git a/drivers/mmc/pci_mmc.c b/drivers/mmc/pci_mmc.c
index fd5dd22..b9ab064 100644
--- a/drivers/mmc/pci_mmc.c
+++ b/drivers/mmc/pci_mmc.c
@@ -53,6 +53,7 @@
 	host->ioaddr = (void *)dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
 					      PCI_REGION_MEM);
 	host->name = dev->name;
+	host->cd_gpio = priv->cd_gpio;
 	host->mmc = &plat->mmc;
 	host->mmc->dev = dev;
 	ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0);
@@ -68,8 +69,11 @@
 {
 	if (CONFIG_IS_ENABLED(DM_GPIO)) {
 		struct pci_mmc_priv *priv = dev_get_priv(dev);
+		int ret;
 
-		gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN);
+		ret = gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio,
+					   GPIOD_IS_IN);
+		log_debug("cd-gpio %s done, ret=%d\n", dev->name, ret);
 	}
 
 	return 0;
diff --git a/drivers/mtd/spi/sf-uclass.c b/drivers/mtd/spi/sf-uclass.c
index 12d1321..cfce00e 100644
--- a/drivers/mtd/spi/sf-uclass.c
+++ b/drivers/mtd/spi/sf-uclass.c
@@ -31,6 +31,15 @@
 	return log_ret(sf_get_ops(dev)->erase(dev, offset, len));
 }
 
+int spl_flash_get_sw_write_prot(struct udevice *dev)
+{
+	struct dm_spi_flash_ops *ops = sf_get_ops(dev);
+
+	if (!ops->get_sw_write_prot)
+		return -ENOSYS;
+	return log_ret(ops->get_sw_write_prot(dev));
+}
+
 /*
  * TODO(sjg@chromium.org): This is an old-style function. We should remove
  * it when all SPI flash drivers use dm
@@ -46,11 +55,6 @@
 	return dev_get_uclass_priv(dev);
 }
 
-void spi_flash_free(struct spi_flash *flash)
-{
-	device_remove(flash->spi->dev, DM_REMOVE_NORMAL);
-}
-
 int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs,
 			   unsigned int max_hz, unsigned int spi_mode,
 			   struct udevice **devp)
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index 9ceff0e..786301b 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -75,6 +75,10 @@
 #define JEDEC_MFR(info)	((info)->id[0])
 #define JEDEC_ID(info)		(((info)->id[1]) << 8 | ((info)->id[2]))
 
+/* Get software write-protect value (BP bits) */
+int spi_flash_cmd_get_sw_write_prot(struct spi_flash *flash);
+
+
 #if CONFIG_IS_ENABLED(SPI_FLASH_MTD)
 int spi_flash_mtd_register(struct spi_flash *flash);
 void spi_flash_mtd_unregister(void);
diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c
index 6c87434..3befbe9 100644
--- a/drivers/mtd/spi/sf_probe.c
+++ b/drivers/mtd/spi/sf_probe.c
@@ -130,6 +130,13 @@
 	return mtd->_erase(mtd, &instr);
 }
 
+static int spi_flash_std_get_sw_write_prot(struct udevice *dev)
+{
+	struct spi_flash *flash = dev_get_uclass_priv(dev);
+
+	return spi_flash_cmd_get_sw_write_prot(flash);
+}
+
 int spi_flash_std_probe(struct udevice *dev)
 {
 	struct spi_slave *slave = dev_get_parent_priv(dev);
@@ -153,6 +160,7 @@
 	.read = spi_flash_std_read,
 	.write = spi_flash_std_write,
 	.erase = spi_flash_std_erase,
+	.get_sw_write_prot = spi_flash_std_get_sw_write_prot,
 };
 
 static const struct udevice_id spi_flash_std_ids[] = {
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index e0efebc..a662553 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -2647,3 +2647,14 @@
 
 	return 0;
 }
+
+/* U-Boot specific functions, need to extend MTD to support these */
+int spi_flash_cmd_get_sw_write_prot(struct spi_nor *nor)
+{
+	int sr = read_sr(nor);
+
+	if (sr < 0)
+		return sr;
+
+	return (sr >> 2) & 7;
+}
diff --git a/drivers/mtd/spi/spi-nor-tiny.c b/drivers/mtd/spi/spi-nor-tiny.c
index 07c8c7b..1d5861d 100644
--- a/drivers/mtd/spi/spi-nor-tiny.c
+++ b/drivers/mtd/spi/spi-nor-tiny.c
@@ -809,3 +809,9 @@
 
 	return 0;
 }
+
+/* U-Boot specific functions, need to extend MTD to support these */
+int spi_flash_cmd_get_sw_write_prot(struct spi_nor *nor)
+{
+	return -ENOTSUPP;
+}
diff --git a/drivers/pci/pci_rom.c b/drivers/pci/pci_rom.c
index a14a4db..7bad4c8 100644
--- a/drivers/pci/pci_rom.c
+++ b/drivers/pci/pci_rom.c
@@ -349,13 +349,10 @@
 	}
 
 	/* Use double buffering if enabled */
-	if (IS_ENABLED(CONFIG_VIDEO_COPY)) {
-		if (!plat->base)
-			return log_msg_ret("copy", -ENFILE);
+	if (IS_ENABLED(CONFIG_VIDEO_COPY) && plat->base)
 		plat->copy_base = vesa->phys_base_ptr;
-	} else {
+	else
 		plat->base = vesa->phys_base_ptr;
-	}
 	log_debug("base = %lx, copy_base = %lx\n", plat->base, plat->copy_base);
 	plat->size = vesa->bytes_per_scanline * vesa->y_resolution;
 
diff --git a/drivers/serial/serial_coreboot.c b/drivers/serial/serial_coreboot.c
index 88c8209..de09c86 100644
--- a/drivers/serial/serial_coreboot.c
+++ b/drivers/serial/serial_coreboot.c
@@ -9,7 +9,7 @@
 #include <dm.h>
 #include <ns16550.h>
 #include <serial.h>
-#include <asm/arch/sysinfo.h>
+#include <asm/cb_sysinfo.h>
 
 static int coreboot_of_to_plat(struct udevice *dev)
 {
diff --git a/drivers/sound/tegra_i2s.c b/drivers/sound/tegra_i2s.c
index 5cf8225..932f737 100644
--- a/drivers/sound/tegra_i2s.c
+++ b/drivers/sound/tegra_i2s.c
@@ -4,7 +4,6 @@
  * Written by Simon Glass <sjg@chromium.org>
  */
 #define LOG_CATEGORY UCLASS_I2S
-#define LOG_DEBUG
 
 #include <common.h>
 #include <dm.h>
diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c
index 7155d4a..ee30110 100644
--- a/drivers/spi/spi-uclass.c
+++ b/drivers/spi/spi-uclass.c
@@ -11,6 +11,7 @@
 #include <log.h>
 #include <malloc.h>
 #include <spi.h>
+#include <spi-mem.h>
 #include <dm/device_compat.h>
 #include <asm/global_data.h>
 #include <dm/device-internal.h>
@@ -199,6 +200,16 @@
 			ops->set_mode += gd->reloc_off;
 		if (ops->cs_info)
 			ops->cs_info += gd->reloc_off;
+		if (ops->mem_ops) {
+			struct spi_controller_mem_ops *mem_ops =
+				(struct spi_controller_mem_ops *)ops->mem_ops;
+			if (mem_ops->adjust_op_size)
+				mem_ops->adjust_op_size += gd->reloc_off;
+			if (mem_ops->supports_op)
+				mem_ops->supports_op += gd->reloc_off;
+			if (mem_ops->exec_op)
+				mem_ops->exec_op += gd->reloc_off;
+		}
 		reloc_done++;
 	}
 #endif
diff --git a/drivers/timer/sandbox_timer.c b/drivers/timer/sandbox_timer.c
index 2075cd4..c846bfb 100644
--- a/drivers/timer/sandbox_timer.c
+++ b/drivers/timer/sandbox_timer.c
@@ -38,7 +38,8 @@
 {
 	struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 
-	if (dev_read_bool(dev, "sandbox,timebase-frequency-fallback"))
+	if (CONFIG_IS_ENABLED(CPU) &&
+	    dev_read_bool(dev, "sandbox,timebase-frequency-fallback"))
 		return timer_timebase_fallback(dev);
 	else if (!uc_priv->clock_rate)
 		uc_priv->clock_rate = SANDBOX_TIMER_RATE;
diff --git a/drivers/timer/timer-uclass.c b/drivers/timer/timer-uclass.c
index 6f00a5d..73b4a5c 100644
--- a/drivers/timer/timer-uclass.c
+++ b/drivers/timer/timer-uclass.c
@@ -83,11 +83,7 @@
 	return 0;
 }
 
-/*
- * TODO: should be CONFIG_IS_ENABLED(CPU), but the SPL config has _SUPPORT on
- * the end...
- */
-#if defined(CONFIG_CPU) || defined(CONFIG_SPL_CPU_SUPPORT)
+#if CONFIG_IS_ENABLED(CPU)
 int timer_timebase_fallback(struct udevice *dev)
 {
 	struct udevice *cpu;
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 667157c..63ae2ba 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -241,7 +241,7 @@
 
 config VIDEO_COREBOOT
 	bool "Enable coreboot framebuffer driver support"
-	depends on X86 && SYS_COREBOOT
+	depends on X86
 	help
 	  Turn on this option to enable a framebuffer driver when U-Boot is
 	  loaded by coreboot where the graphics device is configured by
diff --git a/drivers/video/coreboot.c b/drivers/video/coreboot.c
index 0a5fb08..7237542 100644
--- a/drivers/video/coreboot.c
+++ b/drivers/video/coreboot.c
@@ -5,9 +5,10 @@
 
 #include <common.h>
 #include <dm.h>
+#include <init.h>
 #include <vbe.h>
 #include <video.h>
-#include <asm/arch/sysinfo.h>
+#include <asm/cb_sysinfo.h>
 
 static int save_vesa_mode(struct cb_framebuffer *fb,
 			  struct vesa_mode_info *vesa)
@@ -17,7 +18,7 @@
 	 * running on the serial console.
 	 */
 	if (!fb)
-		return -ENXIO;
+		return log_msg_ret("save", -ENXIO);
 
 	vesa->x_resolution = fb->x_resolution;
 	vesa->y_resolution = fb->y_resolution;
@@ -44,16 +45,23 @@
 	struct vesa_mode_info *vesa = &mode_info.vesa;
 	int ret;
 
+	if (ll_boot_init())
+		return log_msg_ret("ll", -ENODEV);
+
 	printf("Video: ");
 
 	/* Initialize vesa_mode_info structure */
 	ret = save_vesa_mode(fb, vesa);
-	if (ret)
+	if (ret) {
+		ret = log_msg_ret("save", ret);
 		goto err;
+	}
 
 	ret = vbe_setup_video_priv(vesa, uc_priv, plat);
-	if (ret)
+	if (ret) {
+		ret = log_msg_ret("setup", ret);
 		goto err;
+	}
 
 	printf("%dx%dx%d\n", uc_priv->xsize, uc_priv->ysize,
 	       vesa->bits_per_pixel);
@@ -61,7 +69,7 @@
 	return 0;
 
 err:
-	printf("No video mode configured in coreboot!\n");
+	printf("No video mode configured in coreboot (err=%d)\n", ret);
 	return ret;
 }
 
diff --git a/fs/cbfs/cbfs.c b/fs/cbfs/cbfs.c
index 9007aa7..415ea28 100644
--- a/fs/cbfs/cbfs.c
+++ b/fs/cbfs/cbfs.c
@@ -79,6 +79,57 @@
 	dest->offset = be32_to_cpu(src->offset);
 }
 
+/**
+ * fill_node() - Fill a node struct with information from the CBFS
+ *
+ * @node: Node to fill
+ * @start: Pointer to the start of the CBFS file in memory
+ * @header: Pointer to the header information (in our enddianess)
+ * @return 0 if OK, -EBADF if the header is too small
+ */
+static int fill_node(struct cbfs_cachenode *node, void *start,
+		     struct cbfs_fileheader *header)
+{
+	uint name_len;
+	uint offset;
+
+	/* Check the header is large enough */
+	if (header->offset < sizeof(struct cbfs_fileheader))
+		return -EBADF;
+
+	node->next = NULL;
+	node->type = header->type;
+	node->data = start + header->offset;
+	node->data_length = header->len;
+	name_len = header->offset - sizeof(struct cbfs_fileheader);
+	node->name = start + sizeof(struct cbfs_fileheader);
+	node->name_length = name_len;
+	node->attr_offset = header->attributes_offset;
+	node->comp_algo = CBFS_COMPRESS_NONE;
+	node->decomp_size = 0;
+
+	for (offset = node->attr_offset; offset < header->offset;) {
+		const struct cbfs_file_attribute *attr;
+		uint tag, len;
+
+		attr = start + offset;
+		tag = be32_to_cpu(attr->tag);
+		len = be32_to_cpu(attr->len);
+		if (tag == CBFS_FILE_ATTR_TAG_COMPRESSION) {
+			struct cbfs_file_attr_compression *comp;
+
+			comp = start + offset;
+			node->comp_algo = be32_to_cpu(comp->compression);
+			node->decomp_size =
+				be32_to_cpu(comp->decompressed_size);
+		}
+
+		offset += len;
+	}
+
+	return 0;
+}
+
 /*
  * Given a starting position in memory, scan forward, bounded by a size, and
  * find the next valid CBFS file. No memory is allocated by this function. The
@@ -87,7 +138,7 @@
  * @param start		The location in memory to start from.
  * @param size		The size of the memory region to search.
  * @param align		The alignment boundaries to check on.
- * @param new_node	A pointer to the file structure to load.
+ * @param node	A pointer to the file structure to load.
  * @param used		A pointer to the count of of bytes scanned through,
  *			including the file if one is found.
  *
@@ -95,7 +146,7 @@
  *	is found.
  */
 static int file_cbfs_next_file(struct cbfs_priv *priv, void *start, int size,
-			       int align, struct cbfs_cachenode *new_node,
+			       int align, struct cbfs_cachenode *node,
 			       int *used)
 {
 	struct cbfs_fileheader header;
@@ -104,8 +155,7 @@
 
 	while (size >= align) {
 		const struct cbfs_fileheader *file_header = start;
-		u32 name_len;
-		u32 step;
+		int ret;
 
 		/* Check if there's a file here. */
 		if (memcmp(good_file_magic, &file_header->magic,
@@ -117,25 +167,13 @@
 		}
 
 		swap_file_header(&header, file_header);
-		if (header.offset < sizeof(struct cbfs_fileheader)) {
+		ret = fill_node(node, start, &header);
+		if (ret) {
 			priv->result = CBFS_BAD_FILE;
-			return -EBADF;
+			return log_msg_ret("fill", ret);
 		}
-		new_node->next = NULL;
-		new_node->type = header.type;
-		new_node->data = start + header.offset;
-		new_node->data_length = header.len;
-		name_len = header.offset - sizeof(struct cbfs_fileheader);
-		new_node->name = (char *)file_header +
-				sizeof(struct cbfs_fileheader);
-		new_node->name_length = name_len;
-		new_node->attributes_offset = header.attributes_offset;
 
-		step = header.len;
-		if (step % align)
-			step = step + align - step % align;
-
-		*used += step;
+		*used += ALIGN(header.len, align);
 		return 0;
 	}
 
@@ -146,7 +184,7 @@
 static int file_cbfs_fill_cache(struct cbfs_priv *priv, int size, int align)
 {
 	struct cbfs_cachenode *cache_node;
-	struct cbfs_cachenode *new_node;
+	struct cbfs_cachenode *node;
 	struct cbfs_cachenode **cache_tail = &priv->file_cache;
 	void *start;
 
@@ -164,21 +202,20 @@
 		int used;
 		int ret;
 
-		new_node = (struct cbfs_cachenode *)
-				malloc(sizeof(struct cbfs_cachenode));
-		if (!new_node)
+		node = malloc(sizeof(struct cbfs_cachenode));
+		if (!node)
 			return -ENOMEM;
-		ret = file_cbfs_next_file(priv, start, size, align, new_node,
+		ret = file_cbfs_next_file(priv, start, size, align, node,
 					  &used);
 
 		if (ret < 0) {
-			free(new_node);
+			free(node);
 			if (ret == -ENOENT)
 				break;
 			return ret;
 		}
-		*cache_tail = new_node;
-		cache_tail = &new_node->next;
+		*cache_tail = node;
+		cache_tail = &node->next;
 
 		size -= used;
 		start += used;
@@ -276,18 +313,26 @@
 	return cbfs_init(&cbfs_s, end_of_rom);
 }
 
-int cbfs_init_mem(ulong base, struct cbfs_priv **privp)
+int cbfs_init_mem(ulong base, ulong size, bool require_hdr,
+		  struct cbfs_priv **privp)
 {
 	struct cbfs_priv priv_s, *priv = &priv_s;
 	int ret;
 
 	/*
-	 * Use a local variable to start with until we know that the CBFS is
-	 * valid.
+	 * Use a local variable to start with until we know that the * CBFS is
+	 * valid. Note that size is detected from the header, if present,
+	 * meaning the parameter is ignored.
 	 */
 	ret = cbfs_load_header_ptr(priv, base);
-	if (ret)
-		return ret;
+	if (ret) {
+		if (require_hdr || size == CBFS_SIZE_UNKNOWN)
+			return ret;
+		memset(priv, '\0', sizeof(struct cbfs_priv));
+		priv->header.rom_size = size;
+		priv->header.align = CBFS_ALIGN_SIZE;
+		priv->start = (void *)base;
+	}
 
 	ret = file_cbfs_fill_cache(priv, priv->header.rom_size,
 				   priv->header.align);
@@ -317,6 +362,17 @@
 	}
 }
 
+const struct cbfs_cachenode *cbfs_get_first(const struct cbfs_priv *priv)
+{
+	return priv->file_cache;
+}
+
+void cbfs_get_next(const struct cbfs_cachenode **filep)
+{
+	if (*filep)
+		*filep = (*filep)->next;
+}
+
 const struct cbfs_cachenode *file_cbfs_get_first(void)
 {
 	struct cbfs_priv *priv = &cbfs_s;
diff --git a/include/cbfs.h b/include/cbfs.h
index 5f296d6..ae94f1d 100644
--- a/include/cbfs.h
+++ b/include/cbfs.h
@@ -9,6 +9,8 @@
 #include <compiler.h>
 #include <linux/compiler.h>
 
+struct cbfs_priv;
+
 enum cbfs_result {
 	CBFS_SUCCESS = 0,
 	CBFS_NOT_INITIALIZED,
@@ -42,6 +44,8 @@
 
 enum {
 	CBFS_HEADER_MAGIC	= 0x4f524243,
+	CBFS_SIZE_UNKNOWN	= 0xffffffff,
+	CBFS_ALIGN_SIZE		= 0x40,
 };
 
 /**
@@ -68,8 +72,54 @@
 	/* offset to struct cbfs_file_attribute or 0 */
 	u32 attributes_offset;
 	u32 offset;
+	char filename[];
 } __packed;
 
+/**
+ * These are standard values for the known compression alogrithms that coreboot
+ * knows about for stages and payloads.  Of course, other CBFS users can use
+ * whatever values they want, as long as they understand them.
+ */
+#define CBFS_COMPRESS_NONE  0
+#define CBFS_COMPRESS_LZMA  1
+#define CBFS_COMPRESS_LZ4   2
+
+/*
+ * Depending on how the header was initialized, it may be backed with 0x00 or
+ * 0xff, so support both
+ */
+#define CBFS_FILE_ATTR_TAG_UNUSED 0
+#define CBFS_FILE_ATTR_TAG_UNUSED2 0xffffffff
+#define CBFS_FILE_ATTR_TAG_COMPRESSION 0x42435a4c
+#define CBFS_FILE_ATTR_TAG_HASH 0x68736148
+
+/*
+ * The common fields of extended cbfs file attributes. Attributes are expected
+ * to start with tag/len, then append their specific fields
+ */
+struct cbfs_file_attribute {
+	u32 tag;
+	/* len covers the whole structure, incl. tag and len */
+	u32 len;
+	u8 data[0];
+} __packed;
+
+struct cbfs_file_attr_compression {
+	u32 tag;
+	u32 len;
+	/* whole file compression format. 0 if no compression. */
+	u32 compression;
+	u32 decompressed_size;
+} __packed;
+
+struct cbfs_file_attr_hash {
+	u32 tag;
+	u32 len;
+	u32 hash_type;
+	/* hash_data is len - sizeof(struct) bytes */
+	u8  hash_data[];
+} __packed;
+
 struct cbfs_cachenode {
 	struct cbfs_cachenode *next;
 	void *data;
@@ -77,7 +127,9 @@
 	u32 type;
 	u32 data_length;
 	u32 name_length;
-	u32 attributes_offset;
+	u32 attr_offset;
+	u32 comp_algo;
+	u32 decomp_size;
 };
 
 /**
@@ -111,6 +163,21 @@
 const struct cbfs_header *file_cbfs_get_header(void);
 
 /**
+ * cbfs_get_first() - Get the first file in a CBFS
+ *
+ * @return pointer to first file, or NULL if it is empty
+ */
+const struct cbfs_cachenode *cbfs_get_first(const struct cbfs_priv *priv);
+
+/**
+ * cbfs_get_next() - Get the next file in a CBFS
+ *
+ * @filep: Pointer to current file; updated to point to the next file, if any,
+ *	else NULL
+ */
+void cbfs_get_next(const struct cbfs_cachenode **filep);
+
+/**
  * file_cbfs_get_first() - Get a handle for the first file in CBFS.
  *
  * @return A handle for the first file in CBFS, NULL on error.
@@ -133,8 +200,6 @@
  */
 const struct cbfs_cachenode *file_cbfs_find(const char *name);
 
-struct cbfs_priv;
-
 /**
  * cbfs_find_file() - Find a file in a given CBFS
  *
@@ -149,11 +214,13 @@
  * cbfs_init_mem() - Set up a new CBFS
  *
  * @base: Base address of CBFS
+ * @size: Size of CBFS if known, else CBFS_SIZE_UNKNOWN
+ * @require_header: true to read a header at the start, false to not require one
  * @cbfsp: Returns a pointer to CBFS on success
  * @return 0 if OK, -ve on error
  */
-int cbfs_init_mem(ulong base, struct cbfs_priv **privp);
-
+int cbfs_init_mem(ulong base, ulong size, bool require_hdr,
+		  struct cbfs_priv **privp);
 
 /***************************************************************************/
 /* All of the functions below can be used without first initializing CBFS. */
diff --git a/include/command.h b/include/command.h
index 747f8f8..137cfbc 100644
--- a/include/command.h
+++ b/include/command.h
@@ -389,6 +389,14 @@
 		return 0;						\
 	}
 
+#define _CMD_REMOVE_REP(_name, _cmd)					\
+	int __remove_ ## _name(void)					\
+	{								\
+		if (0)							\
+			_cmd(NULL, 0, 0, NULL, NULL);			\
+		return 0;						\
+	}
+
 #define U_BOOT_CMDREP_MKENT_COMPLETE(_name, _maxargs, _cmd_rep,		\
 				     _usage, _help, _comp)		\
 		{ #_name, _maxargs, 0 ? _cmd_rep : NULL, NULL, _usage,	\
@@ -405,7 +413,7 @@
 
 #define U_BOOT_CMDREP_COMPLETE(_name, _maxargs, _cmd_rep, _usage,	\
 			       _help, _comp)				\
-	_CMD_REMOVE(sub_ ## _name, _cmd_rep)
+	_CMD_REMOVE_REP(sub_ ## _name, _cmd_rep)
 
 #endif /* CONFIG_CMDLINE */
 
diff --git a/include/configs/chromebook_coral.h b/include/configs/chromebook_coral.h
index 6e8e8ec..00760b8 100644
--- a/include/configs/chromebook_coral.h
+++ b/include/configs/chromebook_coral.h
@@ -12,13 +12,13 @@
 
 #define CONFIG_BOOTCOMMAND	\
 	"tpm init; tpm startup TPM2_SU_CLEAR; " \
-	"read mmc 2:2 100000 0 80; setexpr loader *001004f0; " \
+	"read mmc 0:2 100000 0 80; setexpr loader *001004f0; " \
 	"setexpr size *00100518; setexpr blocks $size / 200; " \
-	"read mmc 2:2 100000 80 $blocks; setexpr setup $loader - 1000; " \
+	"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 2:2 uuid; then " \
+	"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"
diff --git a/include/dm/of_extra.h b/include/dm/of_extra.h
index ca15df2..fc4f974 100644
--- a/include/dm/of_extra.h
+++ b/include/dm/of_extra.h
@@ -11,7 +11,11 @@
 
 enum fmap_compress_t {
 	FMAP_COMPRESS_NONE,
+	FMAP_COMPRESS_LZMA,
 	FMAP_COMPRESS_LZ4,
+
+	FMAP_COMPRESS_COUNT,
+	FMAP_COMPRESS_UNKNOWN,
 };
 
 enum fmap_hash_t {
@@ -30,6 +34,10 @@
 	enum fmap_hash_t hash_algo;		/* Hash algorithm */
 	const uint8_t *hash;			/* Hash value */
 	int hash_size;				/* Hash size */
+	/* Node pointer if CBFS, else NULL */
+	const struct cbfs_cachenode *cbfs_node;
+	/* Hash node pointer if CBFS, else NULL */
+	const struct cbfs_cachenode *cbfs_hash_node;
 };
 
 /**
diff --git a/include/image.h b/include/image.h
index 138c83d..bcd126d 100644
--- a/include/image.h
+++ b/include/image.h
@@ -886,6 +886,11 @@
 }
 static inline int image_check_arch(const image_header_t *hdr, uint8_t arch)
 {
+#ifndef USE_HOSTCC
+	/* Let's assume that sandbox can load any architecture */
+	if (IS_ENABLED(CONFIG_SANDBOX))
+		return true;
+#endif
 	return (image_get_arch(hdr) == arch) ||
 		(image_get_arch(hdr) == IH_ARCH_ARM && arch == IH_ARCH_ARM64);
 }
diff --git a/include/malloc.h b/include/malloc.h
index e15e528..024b18b 100644
--- a/include/malloc.h
+++ b/include/malloc.h
@@ -880,6 +880,8 @@
 
 #else
 
+void malloc_simple_info(void);
+
 #if CONFIG_IS_ENABLED(SYS_MALLOC_SIMPLE)
 #define malloc malloc_simple
 #define realloc realloc_simple
@@ -887,7 +889,6 @@
 static inline void free(void *ptr) {}
 void *calloc(size_t nmemb, size_t size);
 void *realloc_simple(void *ptr, size_t size);
-void malloc_simple_info(void);
 #else
 
 # ifdef USE_DL_PREFIX
diff --git a/include/sandboxblockdev.h b/include/sandboxblockdev.h
index c1f0afb..4006e94 100644
--- a/include/sandboxblockdev.h
+++ b/include/sandboxblockdev.h
@@ -14,6 +14,13 @@
 	int fd;
 };
 
-int host_dev_bind(int dev, char *filename);
+/**
+ * host_dev_bind() - Bind or unbind a device
+ *
+ * @dev: Device number (0=first slot)
+ * @filename: Host filename to use, or NULL to unbind
+ * @removable: true if the block device should mark itself as removable
+ */
+int host_dev_bind(int dev, char *filename, bool removable);
 
 #endif
diff --git a/include/smbios.h b/include/smbios.h
index ecc4fd1..ffeefb4 100644
--- a/include/smbios.h
+++ b/include/smbios.h
@@ -14,6 +14,10 @@
 #define SMBIOS_MAJOR_VER	3
 #define SMBIOS_MINOR_VER	0
 
+enum {
+	SMBIOS_STR_MAX	= 64,	/* Maximum length allowed for a string */
+};
+
 /* SMBIOS structure types */
 enum {
 	SMBIOS_BIOS_INFORMATION = 0,
@@ -269,4 +273,20 @@
  */
 int smbios_update_version(const char *version);
 
+/**
+ * smbios_update_version_full() - Update the version string
+ *
+ * This can be called after the SMBIOS tables are written (e.g. after the U-Boot
+ * main loop has started) to update the BIOS version string (SMBIOS table 0).
+ * It scans for the correct place to put the version, so does not need U-Boot
+ * to have actually written the tables itself (e.g. if a previous bootloader
+ * did it).
+ *
+ * @smbios_tab: Start of SMBIOS tables
+ * @version: New version string to use
+ * @return 0 if OK, -ENOENT if no version string was previously written,
+ *	-ENOSPC if the new string is too large to fit
+ */
+int smbios_update_version_full(void *smbios_tab, const char *version);
+
 #endif /* _SMBIOS_H_ */
diff --git a/include/spi_flash.h b/include/spi_flash.h
index 85cae32..3d747c9 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -35,6 +35,19 @@
 	int (*write)(struct udevice *dev, u32 offset, size_t len,
 		     const void *buf);
 	int (*erase)(struct udevice *dev, u32 offset, size_t len);
+	/**
+	 * get_sw_write_prot() - Check state of software write-protect feature
+	 *
+	 * SPI flash chips can lock a region of the flash defined by a
+	 * 'protected area'. This function checks if this protected area is
+	 * defined.
+	 *
+	 * @dev:	SPI flash device
+	 * @return 0 if no region is write-protected, 1 if a region is
+	 *	write-protected, -ENOSYS if the driver does not implement this,
+	 *	other -ve value on error
+	 */
+	int (*get_sw_write_prot)(struct udevice *dev);
 };
 
 /* Access the serial operations for a device */
@@ -77,6 +90,20 @@
 int spi_flash_erase_dm(struct udevice *dev, u32 offset, size_t len);
 
 /**
+ * spl_flash_get_sw_write_prot() - Check state of software write-protect feature
+ *
+ * SPI flash chips can lock a region of the flash defined by a
+ * 'protected area'. This function checks if this protected area is
+ * defined.
+ *
+ * @dev:	SPI flash device
+ * @return 0 if no region is write-protected, 1 if a region is
+ *	write-protected, -ENOSYS if the driver does not implement this,
+ *	other -ve value on error
+ */
+int spl_flash_get_sw_write_prot(struct udevice *dev);
+
+/**
  * spi_flash_std_probe() - Probe a SPI flash device
  *
  * This is the standard internal method for probing a SPI flash device to
@@ -97,7 +124,9 @@
 				  unsigned int max_hz, unsigned int spi_mode);
 
 /* Compatibility function - this is the old U-Boot API */
-void spi_flash_free(struct spi_flash *flash);
+static inline void spi_flash_free(struct spi_flash *flash)
+{
+}
 
 static inline int spi_flash_read(struct spi_flash *flash, u32 offset,
 				 size_t len, void *buf)
diff --git a/include/sysinfo.h b/include/sysinfo.h
index 270ac1b..68fad25 100644
--- a/include/sysinfo.h
+++ b/include/sysinfo.h
@@ -37,9 +37,13 @@
 enum sysinfo_id {
 	SYSINFO_ID_NONE,
 
+	/* For SMBIOS tables */
 	SYSINFO_ID_SMBIOS_SYSTEM_VERSION,
 	SYSINFO_ID_SMBIOS_BASEBOARD_VERSION,
 
+	/* For show_board_info() */
+	SYSINFO_ID_BOARD_MODEL,
+
 	/* First value available for downstream/board used */
 	SYSINFO_ID_USER = 0x1000,
 };
diff --git a/lib/Kconfig b/lib/Kconfig
index 7288340..80ff244 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -642,7 +642,7 @@
 
 config BLOBLIST_TABLES
 	bool "Put tables in a bloblist"
-	depends on X86
+	depends on X86 && BLOBLIST
 	help
 	  Normally tables are placed at address 0xf0000 and can be up to 64KB
 	  long. With this option, tables are instead placed in the bloblist
diff --git a/lib/binman.c b/lib/binman.c
index 6040ec8..530df6a 100644
--- a/lib/binman.c
+++ b/lib/binman.c
@@ -128,8 +128,8 @@
 	if (!ofnode_valid(node))
 		return log_msg_ret("node", -ENOENT);
 	binman->image = node;
-	log_debug("binman: Selected image subnode '%s'\n",
-		  ofnode_get_name(binman->image));
+	log_info("binman: Selected image subnode '%s'\n",
+		 ofnode_get_name(binman->image));
 
 	return 0;
 }
diff --git a/lib/smbios-parser.c b/lib/smbios-parser.c
index b89f988..34203f9 100644
--- a/lib/smbios-parser.c
+++ b/lib/smbios-parser.c
@@ -3,6 +3,8 @@
  * Copyright (C) 2020, Bachmann electronic GmbH
  */
 
+#define LOG_CATEGORY	LOGC_BOOT
+
 #include <common.h>
 #include <smbios.h>
 
@@ -94,3 +96,39 @@
 
 	return string_from_smbios_table(header, index);
 }
+
+int smbios_update_version_full(void *smbios_tab, const char *version)
+{
+	const struct smbios_header *hdr;
+	struct smbios_type0 *bios;
+	uint old_len, len;
+	char *ptr;
+
+	log_info("Updating SMBIOS table at %p\n", smbios_tab);
+	hdr = smbios_header(smbios_tab, SMBIOS_BIOS_INFORMATION);
+	if (!hdr)
+		return log_msg_ret("tab", -ENOENT);
+	bios = (struct smbios_type0 *)hdr;
+	ptr = (char *)smbios_string(hdr, bios->bios_ver);
+	if (!ptr)
+		return log_msg_ret("str", -ENOMEDIUM);
+
+	/*
+	 * This string is supposed to have at least enough bytes and is
+	 * padded with spaces. Update it, taking care not to move the
+	 * \0 terminator, so that other strings in the string table
+	 * are not disturbed. See smbios_add_string()
+	 */
+	old_len = strnlen(ptr, SMBIOS_STR_MAX);
+	len = strnlen(version, SMBIOS_STR_MAX);
+	if (len > old_len)
+		return log_ret(-ENOSPC);
+
+	log_debug("Replacing SMBIOS type 0 version string '%s'\n", ptr);
+	memcpy(ptr, version, len);
+#ifdef LOG_DEBUG
+	print_buffer((ulong)ptr, ptr, 1, old_len + 1, 0);
+#endif
+
+	return 0;
+}
diff --git a/lib/smbios.c b/lib/smbios.c
index 7d463c8..9eb226e 100644
--- a/lib/smbios.c
+++ b/lib/smbios.c
@@ -20,10 +20,6 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-enum {
-	SMBIOS_STR_MAX	= 64,	/* Maximum length allowed for a string */
-};
-
 /**
  * struct smbios_ctx - context for writing SMBIOS tables
  *
diff --git a/test/cmd/test_echo.c b/test/cmd/test_echo.c
index aa5cebc..9d60d7d 100644
--- a/test/cmd/test_echo.c
+++ b/test/cmd/test_echo.c
@@ -44,9 +44,10 @@
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(echo_data); ++i) {
+		ut_silence_console(uts);
 		console_record_reset_enable();
 		ut_assertok(run_command(echo_data[i].cmd, 0));
-		gd->flags &= ~GD_FLG_RECORD;
+		ut_unsilence_console(uts);
 		console_record_readline(uts->actual_str,
 					sizeof(uts->actual_str));
 		ut_asserteq_str(echo_data[i].expected, uts->actual_str);
diff --git a/test/dm/sf.c b/test/dm/sf.c
index cc1fc4d..17d43fe 100644
--- a/test/dm/sf.c
+++ b/test/dm/sf.c
@@ -21,7 +21,7 @@
 /* Simple test of sandbox SPI flash */
 static int dm_test_spi_flash(struct unit_test_state *uts)
 {
-	struct udevice *dev;
+	struct udevice *dev, *emul;
 	int full_size = 0x200000;
 	int size = 0x10000;
 	u8 *src, *dst;
@@ -51,6 +51,14 @@
 	ut_assertok(spi_flash_read_dm(dev, 0, size, dst));
 	ut_asserteq_mem(src, dst, size);
 
+	/* Try the write-protect stuff */
+	ut_assertok(uclass_first_device_err(UCLASS_SPI_EMUL, &emul));
+	ut_asserteq(0, spl_flash_get_sw_write_prot(dev));
+	sandbox_sf_set_block_protect(emul, 1);
+	ut_asserteq(1, spl_flash_get_sw_write_prot(dev));
+	sandbox_sf_set_block_protect(emul, 0);
+	ut_asserteq(0, spl_flash_get_sw_write_prot(dev));
+
 	/* Check mapping */
 	ut_assertok(dm_spi_get_mmap(dev, &map_base, &map_size, &offset));
 	ut_asserteq(0x1000, map_base);
diff --git a/test/lib/test_print.c b/test/lib/test_print.c
index 12972f1..a60a5a5 100644
--- a/test/lib/test_print.c
+++ b/test/lib/test_print.c
@@ -18,12 +18,14 @@
 static int test_print_freq(struct unit_test_state *uts,
 			   uint64_t freq, char *expected)
 {
+	ut_silence_console(uts);
 	console_record_reset_enable();
 	print_freq(freq, ";\n");
-	gd->flags &= ~GD_FLG_RECORD;
+	ut_unsilence_console(uts);
 	console_record_readline(uts->actual_str, sizeof(uts->actual_str));
 	ut_asserteq_str(expected, uts->actual_str);
 	ut_assertok(ut_check_console_end(uts));
+
 	return 0;
 }
 
@@ -46,12 +48,14 @@
 static int test_print_size(struct unit_test_state *uts,
 			   uint64_t freq, char *expected)
 {
+	ut_silence_console(uts);
 	console_record_reset_enable();
 	print_size(freq, ";\n");
-	gd->flags &= ~GD_FLG_RECORD;
+	ut_unsilence_console(uts);
 	console_record_readline(uts->actual_str, sizeof(uts->actual_str));
 	ut_asserteq_str(expected, uts->actual_str);
 	ut_assertok(ut_check_console_end(uts));
+
 	return 0;
 }
 
diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst
index 15314d1..1aa2459 100644
--- a/tools/binman/binman.rst
+++ b/tools/binman/binman.rst
@@ -472,6 +472,13 @@
     information about what needs to be fixed. See missing-blob-help for the
     message for each tag.
 
+no-expanded:
+    By default binman substitutes entries with expanded versions if available,
+    so that a `u-boot` entry type turns into `u-boot-expanded`, for example. The
+    `--no-expanded` command-line option disables this globally. The
+    `no-expanded` property disables this just for a single entry. Put the
+    `no-expanded` boolean property in the node to select this behaviour.
+
 The attributes supported for images and sections are described below. Several
 are similar to those for entries.
 
@@ -555,6 +562,13 @@
     'end-at-4gb' property is not applicable where CONFIG_SYS_TEXT_BASE +
     Image size != 4gb.
 
+align-default:
+    Specifies the default alignment for entries in this section, if they do
+    not specify an alignment. Note that this only applies to top-level entries
+    in the section (direct subentries), not any subentries of those entries.
+    This means that each section must specify its own default alignment, if
+    required.
+
 Examples of the above options can be found in the tests. See the
 tools/binman/test directory.
 
diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
index f6faa15..a91211e 100644
--- a/tools/binman/entries.rst
+++ b/tools/binman/entries.rst
@@ -248,6 +248,19 @@
 
 
 
+Entry: collection: An entry which contains a collection of other entries
+------------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - content: List of phandles to entries to include
+
+This allows reusing the contents of other entries. The contents of the
+listed entries are combined to form this entry. This serves as a useful
+base class for entry types which need to process data from elsewhere in
+the image, not necessarily child entries.
+
+
+
 Entry: cros-ec-rw: A blob entry which contains a Chromium OS read-write EC image
 --------------------------------------------------------------------------------
 
@@ -783,6 +796,8 @@
         file, since the first 16 bytes are skipped when writing.
     name-prefix: Adds a prefix to the name of every entry in the section
         when writing out the map
+    align_default: Default alignment for this section, if no alignment is
+        given in the entry
 
 Properties:
     allow_missing: True if this section permits external blobs to be
@@ -863,7 +878,7 @@
 in the binman README for more information.
 
 Note that this entry is automatically replaced with u-boot-expanded unless
---no-expanded is used.
+--no-expanded is used or the node has a 'no-expanded' property.
 
 
 
@@ -984,7 +999,7 @@
 binman uses that to look up symbols to write into the SPL binary.
 
 Note that this entry is automatically replaced with u-boot-spl-expanded
-unless --no-expanded is used.
+unless --no-expanded is used or the node has a 'no-expanded' property.
 
 
 
@@ -1113,7 +1128,7 @@
 binman uses that to look up symbols to write into the TPL binary.
 
 Note that this entry is automatically replaced with u-boot-tpl-expanded
-unless --no-expanded is used.
+unless --no-expanded is used or the node has a 'no-expanded' property.
 
 
 
diff --git a/tools/binman/entry.py b/tools/binman/entry.py
index 1cfa024..7022271 100644
--- a/tools/binman/entry.py
+++ b/tools/binman/entry.py
@@ -164,7 +164,8 @@
         if obj and expanded:
             # Check whether to use the expanded entry
             new_etype = etype + '-expanded'
-            if obj.UseExpanded(node, etype, new_etype):
+            can_expand = not fdt_util.GetBool(node, 'no-expanded')
+            if can_expand and obj.UseExpanded(node, etype, new_etype):
                 etype = new_etype
             else:
                 obj = None
@@ -200,6 +201,8 @@
         if tools.NotPowerOfTwo(self.align):
             raise ValueError("Node '%s': Alignment %s must be a power of two" %
                              (self._node.path, self.align))
+        if self.section and self.align is None:
+            self.align = self.section.align_default
         self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
         self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
         self.align_size = fdt_util.GetInt(self._node, 'align-size')
@@ -437,6 +440,11 @@
         """Convenience function to raise an error referencing a node"""
         raise ValueError("Node '%s': %s" % (self._node.path, msg))
 
+    def Info(self, msg):
+        """Convenience function to log info referencing a node"""
+        tag = "Info '%s'" % self._node.path
+        tout.Detail('%30s: %s' % (tag, msg))
+
     def Detail(self, msg):
         """Convenience function to log detail referencing a node"""
         tag = "Node '%s'" % self._node.path
@@ -476,9 +484,13 @@
         """
         return self._node.path
 
-    def GetData(self):
+    def GetData(self, required=True):
         """Get the contents of an entry
 
+        Args:
+            required: True if the data must be present, False if it is OK to
+                return None
+
         Returns:
             bytes content of the entry, excluding any padding. If the entry is
                 compressed, the compressed data is returned
diff --git a/tools/binman/etype/cbfs.py b/tools/binman/etype/cbfs.py
index 1daddeb..44db7b9 100644
--- a/tools/binman/etype/cbfs.py
+++ b/tools/binman/etype/cbfs.py
@@ -169,6 +169,7 @@
 
         super().__init__(section, etype, node)
         self._cbfs_arg = fdt_util.GetString(node, 'cbfs-arch', 'x86')
+        self.align_default = None
         self._cbfs_entries = OrderedDict()
         self._ReadSubnodes()
         self.reader = None
diff --git a/tools/binman/etype/collection.py b/tools/binman/etype/collection.py
new file mode 100644
index 0000000..1625575
--- /dev/null
+++ b/tools/binman/etype/collection.py
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2021 Google LLC
+# Written by Simon Glass <sjg@chromium.org>
+#
+
+# Support for a collection of entries from other parts of an image
+
+from collections import OrderedDict
+import os
+
+from binman.entry import Entry
+from dtoc import fdt_util
+
+class Entry_collection(Entry):
+    """An entry which contains a collection of other entries
+
+    Properties / Entry arguments:
+        - content: List of phandles to entries to include
+
+    This allows reusing the contents of other entries. The contents of the
+    listed entries are combined to form this entry. This serves as a useful
+    base class for entry types which need to process data from elsewhere in
+    the image, not necessarily child entries.
+    """
+    def __init__(self, section, etype, node):
+        super().__init__(section, etype, node)
+        self.content = fdt_util.GetPhandleList(self._node, 'content')
+        if not self.content:
+            self.Raise("Collection must have a 'content' property")
+
+    def GetContents(self, required):
+        """Get the contents of this entry
+
+        Args:
+            required: True if the data must be present, False if it is OK to
+                return None
+
+        Returns:
+            bytes content of the entry
+        """
+        # Join up all the data
+        self.Info('Getting contents, required=%s' % required)
+        data = b''
+        for entry_phandle in self.content:
+            entry_data = self.section.GetContentsByPhandle(entry_phandle, self,
+                                                           required)
+            if not required and entry_data is None:
+                self.Info('Contents not available yet')
+                # Data not available yet
+                return None
+            data += entry_data
+
+        self.Info('Returning contents size %x' % len(data))
+
+        return data
+
+    def ObtainContents(self):
+        data = self.GetContents(False)
+        if data is None:
+            return False
+        self.SetContents(data)
+        return True
+
+    def ProcessContents(self):
+        # The blob may have changed due to WriteSymbols()
+        data = self.GetContents(True)
+        return self.ProcessContentsUpdate(data)
diff --git a/tools/binman/etype/mkimage.py b/tools/binman/etype/mkimage.py
index e9f8272..e499775 100644
--- a/tools/binman/etype/mkimage.py
+++ b/tools/binman/etype/mkimage.py
@@ -36,6 +36,7 @@
         super().__init__(section, etype, node)
         self._args = fdt_util.GetString(self._node, 'args').split(' ')
         self._mkimage_entries = OrderedDict()
+        self.align_default = None
         self._ReadSubnodes()
 
     def ObtainContents(self):
diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py
index cce1500..c3bac02 100644
--- a/tools/binman/etype/section.py
+++ b/tools/binman/etype/section.py
@@ -36,6 +36,8 @@
             file, since the first 16 bytes are skipped when writing.
         name-prefix: Adds a prefix to the name of every entry in the section
             when writing out the map
+        align_default: Default alignment for this section, if no alignment is
+            given in the entry
 
     Properties:
         allow_missing: True if this section permits external blobs to be
@@ -76,6 +78,7 @@
             if self._skip_at_start is None:
                 self._skip_at_start = 0
         self._name_prefix = fdt_util.GetString(self._node, 'name-prefix')
+        self.align_default = fdt_util.GetInt(self._node, 'align-default', 0)
         filename = fdt_util.GetString(self._node, 'filename')
         if filename:
             self._filename = filename
@@ -180,7 +183,7 @@
 
         return data
 
-    def _BuildSectionData(self):
+    def _BuildSectionData(self, required):
         """Build the contents of a section
 
         This places all entries at the right place, dealing with padding before
@@ -188,13 +191,20 @@
         pad-before and pad-after properties in the section items) since that is
         handled by the parent section.
 
+        Args:
+            required: True if the data must be present, False if it is OK to
+                return None
+
         Returns:
             Contents of the section (bytes)
         """
         section_data = b''
 
         for entry in self._entries.values():
-            data = self.GetPaddedDataForEntry(entry, entry.GetData())
+            entry_data = entry.GetData(required)
+            if not required and entry_data is None:
+                return None
+            data = self.GetPaddedDataForEntry(entry, entry_data)
             # Handle empty space before the entry
             pad = (entry.offset or 0) - self._skip_at_start - len(section_data)
             if pad > 0:
@@ -226,18 +236,24 @@
             data = self.GetData()
         return section.GetPaddedDataForEntry(self, data)
 
-    def GetData(self):
+    def GetData(self, required=True):
         """Get the contents of an entry
 
         This builds the contents of the section, stores this as the contents of
         the section and returns it
 
+        Args:
+            required: True if the data must be present, False if it is OK to
+                return None
+
         Returns:
             bytes content of the section, made up for all all of its subentries.
             This excludes any padding. If the section is compressed, the
             compressed data is returned
         """
-        data = self._BuildSectionData()
+        data = self._BuildSectionData(required)
+        if data is None:
+            return None
         self.SetContents(data)
         return data
 
@@ -263,7 +279,7 @@
             self._SortEntries()
         self._ExpandEntries()
 
-        data = self._BuildSectionData()
+        data = self._BuildSectionData(True)
         self.SetContents(data)
 
         self.CheckSize()
@@ -360,16 +376,20 @@
     def GetEntries(self):
         return self._entries
 
-    def GetContentsByPhandle(self, phandle, source_entry):
+    def GetContentsByPhandle(self, phandle, source_entry, required):
         """Get the data contents of an entry specified by a phandle
 
         This uses a phandle to look up a node and and find the entry
-        associated with it. Then it returnst he contents of that entry.
+        associated with it. Then it returns the contents of that entry.
+
+        The node must be a direct subnode of this section.
 
         Args:
             phandle: Phandle to look up (integer)
             source_entry: Entry containing that phandle (used for error
                 reporting)
+            required: True if the data must be present, False if it is OK to
+                return None
 
         Returns:
             data from associated entry (as a string), or None if not found
@@ -379,7 +399,7 @@
             source_entry.Raise("Cannot find node for phandle %d" % phandle)
         for entry in self._entries.values():
             if entry._node == node:
-                return entry.GetData()
+                return entry.GetData(required)
         source_entry.Raise("Cannot find entry for node '%s'" % node.name)
 
     def LookupSymbol(self, sym_name, optional, msg, base_addr, entries=None):
diff --git a/tools/binman/etype/u_boot.py b/tools/binman/etype/u_boot.py
index d2eaba6..e8d180a 100644
--- a/tools/binman/etype/u_boot.py
+++ b/tools/binman/etype/u_boot.py
@@ -25,7 +25,7 @@
     in the binman README for more information.
 
     Note that this entry is automatically replaced with u-boot-expanded unless
-    --no-expanded is used.
+    --no-expanded is used or the node has a 'no-expanded' property.
     """
     def __init__(self, section, etype, node):
         super().__init__(section, etype, node)
diff --git a/tools/binman/etype/u_boot_spl.py b/tools/binman/etype/u_boot_spl.py
index 1c39f98..6f79bf5 100644
--- a/tools/binman/etype/u_boot_spl.py
+++ b/tools/binman/etype/u_boot_spl.py
@@ -32,7 +32,7 @@
     binman uses that to look up symbols to write into the SPL binary.
 
     Note that this entry is automatically replaced with u-boot-spl-expanded
-    unless --no-expanded is used.
+    unless --no-expanded is used or the node has a 'no-expanded' property.
     """
     def __init__(self, section, etype, node):
         super().__init__(section, etype, node)
diff --git a/tools/binman/etype/u_boot_tpl.py b/tools/binman/etype/u_boot_tpl.py
index 95d9bd6..0c575df 100644
--- a/tools/binman/etype/u_boot_tpl.py
+++ b/tools/binman/etype/u_boot_tpl.py
@@ -32,7 +32,7 @@
     binman uses that to look up symbols to write into the TPL binary.
 
     Note that this entry is automatically replaced with u-boot-tpl-expanded
-    unless --no-expanded is used.
+    unless --no-expanded is used or the node has a 'no-expanded' property.
     """
     def __init__(self, section, etype, node):
         super().__init__(section, etype, node)
diff --git a/tools/binman/etype/vblock.py b/tools/binman/etype/vblock.py
index eba5351..c0a6a28 100644
--- a/tools/binman/etype/vblock.py
+++ b/tools/binman/etype/vblock.py
@@ -9,12 +9,13 @@
 from collections import OrderedDict
 import os
 
-from binman.entry import Entry, EntryArg
+from binman.entry import EntryArg
+from binman.etype.collection import Entry_collection
 
 from dtoc import fdt_util
 from patman import tools
 
-class Entry_vblock(Entry):
+class Entry_vblock(Entry_collection):
     """An entry which contains a Chromium OS verified boot block
 
     Properties / Entry arguments:
@@ -37,9 +38,6 @@
     """
     def __init__(self, section, etype, node):
         super().__init__(section, etype, node)
-        self.content = fdt_util.GetPhandleList(self._node, 'content')
-        if not self.content:
-            self.Raise("Vblock must have a 'content' property")
         (self.keydir, self.keyblock, self.signprivate, self.version,
          self.kernelkey, self.preamble_flags) = self.GetEntryArgsOrProps([
             EntryArg('keydir', str),
@@ -49,15 +47,21 @@
             EntryArg('kernelkey', str),
             EntryArg('preamble-flags', int)])
 
-    def GetVblock(self):
+    def GetVblock(self, required):
+        """Get the contents of this entry
+
+        Args:
+            required: True if the data must be present, False if it is OK to
+                return None
+
+        Returns:
+            bytes content of the entry, which is the signed vblock for the
+                provided data
+        """
         # Join up the data files to be signed
-        input_data = b''
-        for entry_phandle in self.content:
-            data = self.section.GetContentsByPhandle(entry_phandle, self)
-            if data is None:
-                # Data not available yet
-                return False
-            input_data += data
+        input_data = self.GetContents(required)
+        if input_data is None:
+            return None
 
         uniq = self.GetUniqueName()
         output_fname = tools.GetOutputFilename('vblock.%s' % uniq)
@@ -79,13 +83,13 @@
         return tools.ReadFile(output_fname)
 
     def ObtainContents(self):
-        data = self.GetVblock()
-        if data is False:
+        data = self.GetVblock(False)
+        if data is None:
             return False
         self.SetContents(data)
         return True
 
     def ProcessContents(self):
         # The blob may have changed due to WriteSymbols()
-        data = self.GetVblock()
+        data = self.GetVblock(True)
         return self.ProcessContentsUpdate(data)
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index 81c213a..89fe661 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -1344,13 +1344,19 @@
         data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
         self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
 
-    def checkSymbols(self, dts, base_data, u_boot_offset):
+    def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
+                     use_expanded=False):
         """Check the image contains the expected symbol values
 
         Args:
             dts: Device tree file to use for test
             base_data: Data before and after 'u-boot' section
             u_boot_offset: Offset of 'u-boot' section in image
+            entry_args: Dict of entry args to supply to binman
+                key: arg name
+                value: value of that arg
+            use_expanded: True to use expanded entries where available, e.g.
+                'u-boot-expanded' instead of 'u-boot'
         """
         elf_fname = self.ElfTestFile('u_boot_binman_syms')
         syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
@@ -1359,7 +1365,8 @@
                          addr)
 
         self._SetupSplElf('u_boot_binman_syms')
-        data = self._DoReadFile(dts)
+        data = self._DoReadFileDtb(dts, entry_args=entry_args,
+                                   use_expanded=use_expanded)[0]
         # The image should contain the symbols from u_boot_binman_syms.c
         # Note that image_pos is adjusted by the base address of the image,
         # which is 0x10 in our test image
@@ -1377,7 +1384,7 @@
 
     def testSymbolsNoDtb(self):
         """Test binman can assign symbols embedded in U-Boot SPL"""
-        self.checkSymbols('192_symbols_nodtb.dts',
+        self.checkSymbols('196_symbols_nodtb.dts',
                           U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
                           0x38)
 
@@ -1711,7 +1718,7 @@
         """Test we detect a vblock which has no content to sign"""
         with self.assertRaises(ValueError) as e:
             self._DoReadFile('075_vblock_no_content.dts')
-        self.assertIn("Node '/binman/vblock': Vblock must have a 'content' "
+        self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
                       'property', str(e.exception))
 
     def testVblockBadPhandle(self):
@@ -4460,5 +4467,47 @@
         start += fdt_size + len(U_BOOT_TPL_DATA)
         self.assertEqual(len(data), start)
 
+    def testSymbolsExpanded(self):
+        """Test binman can assign symbols in expanded entries"""
+        entry_args = {
+            'spl-dtb': '1',
+        }
+        self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
+                          U_BOOT_SPL_DTB_DATA, 0x38,
+                          entry_args=entry_args, use_expanded=True)
+
+    def testCollection(self):
+        """Test a collection"""
+        data = self._DoReadFile('198_collection.dts')
+        self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
+                         tools.GetBytes(0xff, 2) + U_BOOT_NODTB_DATA +
+                         tools.GetBytes(0xfe, 3) + U_BOOT_DTB_DATA,
+                         data)
+
+    def testCollectionSection(self):
+        """Test a collection where a section must be built first"""
+        # Sections never have their contents when GetData() is called, but when
+        # _BuildSectionData() is called with required=True, a section will force
+        # building the contents, producing an error is anything is still
+        # missing.
+        data = self._DoReadFile('199_collection_section.dts')
+        section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
+        self.assertEqual(section + U_BOOT_DATA + tools.GetBytes(0xff, 2) +
+                         section + tools.GetBytes(0xfe, 3) + U_BOOT_DATA,
+                         data)
+
+    def testAlignDefault(self):
+        """Test that default alignment works on sections"""
+        data = self._DoReadFile('200_align_default.dts')
+        expected = (U_BOOT_DATA + tools.GetBytes(0, 8 - len(U_BOOT_DATA)) +
+                    U_BOOT_DATA)
+        # Special alignment for section
+        expected += tools.GetBytes(0, 32 - len(expected))
+        # No alignment within the nested section
+        expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
+        # Now the final piece, which should be default-aligned
+        expected += tools.GetBytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
+        self.assertEqual(expected, data)
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tools/binman/test/192_symbols_nodtb.dts b/tools/binman/test/196_symbols_nodtb.dts
similarity index 100%
rename from tools/binman/test/192_symbols_nodtb.dts
rename to tools/binman/test/196_symbols_nodtb.dts
diff --git a/tools/binman/test/197_symbols_expand.dts b/tools/binman/test/197_symbols_expand.dts
new file mode 100644
index 0000000..8aee76d
--- /dev/null
+++ b/tools/binman/test/197_symbols_expand.dts
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		pad-byte = <0xff>;
+		u-boot-spl {
+		};
+
+		u-boot {
+			offset = <0x38>;
+			no-expanded;
+		};
+
+		u-boot-spl2 {
+			type = "u-boot-spl";
+		};
+	};
+};
diff --git a/tools/binman/test/198_collection.dts b/tools/binman/test/198_collection.dts
new file mode 100644
index 0000000..484a1b0
--- /dev/null
+++ b/tools/binman/test/198_collection.dts
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		collection {
+			content = <&u_boot_nodtb &dtb>;
+		};
+		fill {
+			size = <2>;
+			fill-byte = [ff];
+		};
+		u_boot_nodtb: u-boot-nodtb {
+		};
+		fill2 {
+			type = "fill";
+			size = <3>;
+			fill-byte = [fe];
+		};
+		dtb: u-boot-dtb {
+		};
+	};
+};
diff --git a/tools/binman/test/199_collection_section.dts b/tools/binman/test/199_collection_section.dts
new file mode 100644
index 0000000..03a7319
--- /dev/null
+++ b/tools/binman/test/199_collection_section.dts
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		collection {
+			content = <&section &u_boot>;
+		};
+		fill {
+			size = <2>;
+			fill-byte = [ff];
+		};
+		section: section {
+			u-boot-nodtb {
+			};
+			u-boot-dtb {
+			};
+		};
+		fill2 {
+			type = "fill";
+			size = <3>;
+			fill-byte = [fe];
+		};
+		u_boot: u-boot {
+			no-expanded;
+		};
+	};
+};
diff --git a/tools/binman/test/200_align_default.dts b/tools/binman/test/200_align_default.dts
new file mode 100644
index 0000000..1b15577
--- /dev/null
+++ b/tools/binman/test/200_align_default.dts
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		align-default = <8>;
+		u-boot {
+		};
+
+		u-boot-align {
+			type = "u-boot";
+		};
+
+		section {
+			align = <32>;
+			u-boot {
+			};
+
+			u-boot-nodtb {
+			};
+		};
+
+		u-boot-nodtb {
+		};
+	};
+};
diff --git a/tools/dtoc/fdt.py b/tools/dtoc/fdt.py
index 25ce513..3996971 100644
--- a/tools/dtoc/fdt.py
+++ b/tools/dtoc/fdt.py
@@ -103,6 +103,8 @@
     """A device tree property
 
     Properties:
+        node: Node containing this property
+        offset: Offset of the property (None if still to be synced)
         name: Property name (as per the device tree)
         value: Property value as a string of bytes, or a list of strings of
             bytes
@@ -114,7 +116,7 @@
         self.name = name
         self.value = None
         self.bytes = bytes(data)
-        self.dirty = False
+        self.dirty = offset is None
         if not data:
             self.type = Type.BOOL
             self.value = True
@@ -228,9 +230,14 @@
         Raises:
             FdtException if auto_resize is False and there is not enough space
         """
-        if self._offset is None or self.dirty:
+        if self.dirty:
             node = self._node
             fdt_obj = node._fdt._fdt_obj
+            node_name = fdt_obj.get_name(node._offset)
+            if node_name and node_name != node.name:
+                raise ValueError("Internal error, node '%s' name mismatch '%s'" %
+                                 (node.path, node_name))
+
             if auto_resize:
                 while fdt_obj.setprop(node.Offset(), self.name, self.bytes,
                                     (libfdt.NOSPACE,)) == -libfdt.NOSPACE:
@@ -239,13 +246,15 @@
                     fdt_obj.setprop(node.Offset(), self.name, self.bytes)
             else:
                 fdt_obj.setprop(node.Offset(), self.name, self.bytes)
+            self.dirty = False
 
 
 class Node:
     """A device tree node
 
     Properties:
-        offset: Integer offset in the device tree
+        parent: Parent Node
+        offset: Integer offset in the device tree (None if to be synced)
         name: Device tree node tname
         path: Full path to node, along with the node name itself
         _fdt: Device tree object
@@ -324,6 +333,11 @@
         fdt_obj = self._fdt._fdt_obj
         if self._offset != my_offset:
             self._offset = my_offset
+        name = fdt_obj.get_name(self._offset)
+        if name and self.name != name:
+            raise ValueError("Internal error, node '%s' name mismatch '%s'" %
+                             (self.path, name))
+
         offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND)
         for subnode in self.subnodes:
             if subnode.name != fdt_obj.get_name(offset):
@@ -339,8 +353,8 @@
             p = fdt_obj.get_property_by_offset(poffset)
             prop = self.props.get(p.name)
             if not prop:
-                raise ValueError("Internal error, property '%s' missing, "
-                                 'offset %d' % (p.name, poffset))
+                raise ValueError("Internal error, node '%s' property '%s' missing, "
+                                 'offset %d' % (self.path, p.name, poffset))
             prop.RefreshOffset(poffset)
             poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND)
 
@@ -447,8 +461,13 @@
         Args:
             prop_name: Name of property to add
             val: Bytes value of property
+
+        Returns:
+            Prop added
         """
-        self.props[prop_name] = Prop(self, None, prop_name, val)
+        prop = Prop(self, None, prop_name, val)
+        self.props[prop_name] = prop
+        return prop
 
     def AddString(self, prop_name, val):
         """Add a new string property to a node
@@ -459,9 +478,12 @@
         Args:
             prop_name: Name of property to add
             val: String value of property
+
+        Returns:
+            Prop added
         """
         val = bytes(val, 'utf-8')
-        self.AddData(prop_name, val + b'\0')
+        return self.AddData(prop_name, val + b'\0')
 
     def AddInt(self, prop_name, val):
         """Add a new integer property to a node
@@ -472,8 +494,11 @@
         Args:
             prop_name: Name of property to add
             val: Integer value of property
+
+        Returns:
+            Prop added
         """
-        self.AddData(prop_name, struct.pack('>I', val))
+        return self.AddData(prop_name, struct.pack('>I', val))
 
     def AddSubnode(self, name):
         """Add a new subnode to the node
@@ -499,9 +524,13 @@
             auto_resize: Resize the device tree automatically if it does not
                 have enough space for the update
 
+        Returns:
+            True if the node had to be added, False if it already existed
+
         Raises:
             FdtException if auto_resize is False and there is not enough space
         """
+        added = False
         if self._offset is None:
             # The subnode doesn't exist yet, so add it
             fdt_obj = self._fdt._fdt_obj
@@ -515,23 +544,45 @@
             else:
                 offset = fdt_obj.add_subnode(self.parent._offset, self.name)
             self._offset = offset
+            added = True
+
+        # Sync the existing subnodes first, so that we can rely on the offsets
+        # being correct. As soon as we add new subnodes, it pushes all the
+        # existing subnodes up.
+        for node in reversed(self.subnodes):
+            if node._offset is not None:
+                node.Sync(auto_resize)
 
-        # Sync subnodes in reverse so that we don't disturb node offsets for
-        # nodes that are earlier in the DT. This avoids an O(n^2) rescan of
-        # node offsets.
+        # Sync subnodes in reverse so that we get the expected order. Each
+        # new node goes at the start of the subnode list. This avoids an O(n^2)
+        # rescan of node offsets.
+        num_added = 0
         for node in reversed(self.subnodes):
-            node.Sync(auto_resize)
+            if node.Sync(auto_resize):
+                num_added += 1
+        if num_added:
+            # Reorder our list of nodes to put the new ones first, since that's
+            # what libfdt does
+            old_count = len(self.subnodes) - num_added
+            subnodes = self.subnodes[old_count:] + self.subnodes[:old_count]
+            self.subnodes = subnodes
 
-        # Sync properties now, whose offsets should not have been disturbed.
-        # We do this after subnodes, since this disturbs the offsets of these
-        # properties. Note that new properties will have an offset of None here,
-        # which Python 3 cannot sort against int. So use a large value instead
-        # to ensure that the new properties are added first.
+        # Sync properties now, whose offsets should not have been disturbed,
+        # since properties come before subnodes. This is done after all the
+        # subnode processing above, since updating properties can disturb the
+        # offsets of those subnodes.
+        # Properties are synced in reverse order, with new properties added
+        # before existing properties are synced. This ensures that the offsets
+        # of earlier properties are not disturbed.
+        # Note that new properties will have an offset of None here, which
+        # Python cannot sort against int. So use a large value instead so that
+        # new properties are added first.
         prop_list = sorted(self.props.values(),
                            key=lambda prop: prop._offset or 1 << 31,
                            reverse=True)
         for prop in prop_list:
             prop.Sync(auto_resize)
+        return added
 
 
 class Fdt:
@@ -642,8 +693,9 @@
         Raises:
             FdtException if auto_resize is False and there is not enough space
         """
+        self.CheckCache()
         self._root.Sync(auto_resize)
-        self.Invalidate()
+        self.Refresh()
 
     def Pack(self):
         """Pack the device tree down to its minimum size
@@ -652,7 +704,7 @@
         build up in the device tree binary.
         """
         CheckErr(self._fdt_obj.pack(), 'pack')
-        self.Invalidate()
+        self.Refresh()
 
     def GetContents(self):
         """Get the contents of the FDT
@@ -704,11 +756,11 @@
         if self._cached_offsets:
             return
         self.Refresh()
-        self._cached_offsets = True
 
     def Refresh(self):
         """Refresh the offset cache"""
         self._root.Refresh(0)
+        self._cached_offsets = True
 
     def GetStructOffset(self, offset):
         """Get the file offset of a given struct offset
diff --git a/tools/dtoc/test/dtoc_test_simple.dts b/tools/dtoc/test/dtoc_test_simple.dts
index d8ab861..b5c1274 100644
--- a/tools/dtoc/test/dtoc_test_simple.dts
+++ b/tools/dtoc/test/dtoc_test_simple.dts
@@ -56,4 +56,8 @@
 			low-power;
 		};
 	};
+
+	orig-node {
+		orig = <1 23 4>;
+	};
 };
diff --git a/tools/dtoc/test_fdt.py b/tools/dtoc/test_fdt.py
index 1c3a8a2..856392b 100755
--- a/tools/dtoc/test_fdt.py
+++ b/tools/dtoc/test_fdt.py
@@ -154,6 +154,7 @@
     def setUp(self):
         self.dtb = fdt.FdtScan(find_dtb_file('dtoc_test_simple.dts'))
         self.node = self.dtb.GetNode('/spl-test')
+        self.fdt = self.dtb.GetFdtObj()
 
     def testOffset(self):
         """Tests that we can obtain the offset of a node"""
@@ -197,7 +198,7 @@
     def testRefreshExtraNode(self):
         """Test refreshing offsets when an expected node is missing"""
         # Delete it from the device tre, not our tables
-        self.dtb.GetFdtObj().del_node(self.node.Offset())
+        self.fdt.del_node(self.node.Offset())
         with self.assertRaises(ValueError) as e:
             self.dtb.Refresh()
         self.assertIn('Internal error, node name mismatch '
@@ -209,7 +210,7 @@
         del self.node.props['notstring']
         with self.assertRaises(ValueError) as e:
             self.dtb.Refresh()
-        self.assertIn("Internal error, property 'notstring' missing, offset ",
+        self.assertIn("Internal error, node '/spl-test' property 'notstring' missing, offset ",
                       str(e.exception))
 
     def testLookupPhandle(self):
@@ -220,6 +221,66 @@
         target = dtb.GetNode('/phandle-target')
         self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
 
+    def testAddNodeSpace(self):
+        """Test adding a single node when out of space"""
+        self.fdt.pack()
+        self.node.AddSubnode('subnode')
+        with self.assertRaises(libfdt.FdtException) as e:
+            self.dtb.Sync(auto_resize=False)
+        self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
+
+        self.dtb.Sync(auto_resize=True)
+        offset = self.fdt.path_offset('/spl-test/subnode')
+        self.assertTrue(offset > 0)
+
+    def testAddNodes(self):
+        """Test adding various subnode and properies"""
+        node = self.dtb.GetNode('/i2c@0')
+
+        # Add one more node next to the pmic one
+        sn1 = node.AddSubnode('node-one')
+        sn1.AddInt('integer-a', 12)
+        sn1.AddInt('integer-b', 23)
+
+        # Sync so that everything is clean
+        self.dtb.Sync(auto_resize=True)
+
+        # Add two subnodes next to pmic and node-one
+        sn2 = node.AddSubnode('node-two')
+        sn2.AddInt('integer-2a', 34)
+        sn2.AddInt('integer-2b', 45)
+
+        sn3 = node.AddSubnode('node-three')
+        sn3.AddInt('integer-3', 123)
+
+        # Add a property to the node after i2c@0 to check that this is not
+        # disturbed by adding a subnode to i2c@0
+        orig_node = self.dtb.GetNode('/orig-node')
+        orig_node.AddInt('integer-4', 456)
+
+        # Add a property to the pmic node to check that pmic properties are not
+        # disturbed
+        pmic = self.dtb.GetNode('/i2c@0/pmic@9')
+        pmic.AddInt('integer-5', 567)
+
+        self.dtb.Sync(auto_resize=True)
+
+    def testRefreshNameMismatch(self):
+        """Test name mismatch when syncing nodes and properties"""
+        prop = self.node.AddInt('integer-a', 12)
+
+        wrong_offset = self.dtb.GetNode('/i2c@0')._offset
+        self.node._offset = wrong_offset
+        with self.assertRaises(ValueError) as e:
+            self.dtb.Sync()
+        self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
+                      str(e.exception))
+
+        with self.assertRaises(ValueError) as e:
+            self.node.Refresh(wrong_offset)
+        self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
+                      str(e.exception))
+
 
 class TestProp(unittest.TestCase):
     """Test operation of the Prop class"""
@@ -385,17 +446,6 @@
         self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
         self.dtb.Sync(auto_resize=True)
 
-    def testAddNode(self):
-        self.fdt.pack()
-        self.node.AddSubnode('subnode')
-        with self.assertRaises(libfdt.FdtException) as e:
-            self.dtb.Sync(auto_resize=False)
-        self.assertIn('FDT_ERR_NOSPACE', str(e.exception))
-
-        self.dtb.Sync(auto_resize=True)
-        offset = self.fdt.path_offset('/spl-test/subnode')
-        self.assertTrue(offset > 0)
-
     def testAddMore(self):
         """Test various other methods for adding and setting properties"""
         self.node.AddZeroProp('one')