spl: Add support for passing handoff info to U-Boot proper

There is some basic informaton that SPL normally wants to pass through to
U-Boot, such as the SDRAM size and bank information.

Mkae use of the new bloblist structure for this. Add a new 'handoff' blob
which is set up in SPL and passed to U-Boot proper. Also adda  test for
sandbox_spl that checks that this works correctly and a new 'sb' command
to show the information passed from SPL.

Reviewed-by: Tom Rini <trini@konsulko.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/common/board_f.c b/common/board_f.c
index 290e2b0..835b724 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -25,6 +25,9 @@
 #include <post.h>
 #include <relocate.h>
 #include <spi.h>
+#ifdef CONFIG_SPL
+#include <spl.h>
+#endif
 #include <status_led.h>
 #include <sysreset.h>
 #include <timer.h>
@@ -286,6 +289,17 @@
 	return 0;
 }
 
+static int setup_spl_handoff(void)
+{
+#if CONFIG_IS_ENABLED(HANDOFF)
+	gd->spl_handoff = bloblist_find(BLOBLISTT_SPL_HANDOFF,
+					sizeof(struct spl_handoff));
+	debug("Found SPL hand-off info %p\n", gd->spl_handoff);
+#endif
+
+	return 0;
+}
+
 __weak int arch_cpu_init(void)
 {
 	return 0;
@@ -845,6 +859,7 @@
 #ifdef CONFIG_BLOBLIST
 	bloblist_init,
 #endif
+	setup_spl_handoff,
 	initf_console_record,
 #if defined(CONFIG_HAVE_FSP)
 	arch_fsp_init,
diff --git a/common/init/Makefile b/common/init/Makefile
index 4902635..853b56d 100644
--- a/common/init/Makefile
+++ b/common/init/Makefile
@@ -5,3 +5,4 @@
 #
 
 obj-y += board_init.o
+obj-$(CONFIG_$(SPL_TPL_)HANDOFF) += handoff.o
diff --git a/common/init/handoff.c b/common/init/handoff.c
new file mode 100644
index 0000000..e00b43e
--- /dev/null
+++ b/common/init/handoff.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Passing basic information from SPL to U-Boot proper
+ *
+ * Copyright 2018 Google, Inc
+ */
+
+#include <common.h>
+#include <handoff.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void handoff_save_dram(struct spl_handoff *ho)
+{
+	ho->ram_size = gd->ram_size;
+#ifdef CONFIG_NR_DRAM_BANKS
+	{
+		struct bd_info *bd = gd->bd;
+		int i;
+
+		for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
+			ho->ram_bank[i].start = bd->bi_dram[i].start;
+			ho->ram_bank[i].size = bd->bi_dram[i].size;
+		}
+	}
+#endif
+}
+
+void handoff_load_dram_size(struct spl_handoff *ho)
+{
+	gd->ram_size = ho->ram_size;
+}
+
+void handoff_load_dram_banks(struct spl_handoff *ho)
+{
+#ifdef CONFIG_NR_DRAM_BANKS
+	{
+		struct bd_info *bd = gd->bd;
+		int i;
+
+		for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
+			bd->bi_dram[i].start = ho->ram_bank[i].start;
+			bd->bi_dram[i].size = ho->ram_bank[i].size;
+		}
+	}
+#endif
+}
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index 32fa575..0ddbffc 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -25,8 +25,28 @@
 	  supports MMC, NAND and YMODEM and other methods loading of U-Boot
 	  and the Linux Kernel.  If unsure, say Y.
 
+config HANDOFF
+	bool "Pass hand-off information from SPL to U-Boot proper"
+	depends on BLOBLIST
+	help
+	  It is useful to be able to pass information from SPL to U-Boot
+	  proper to preserve state that is known in SPL and is needed in U-Boot.
+	  Enable this to locate the handoff information in U-Boot proper, early
+	  in boot. It is available in gd->handoff. The state state is set up
+	  in SPL (or TPL if that is being used).
+
 if SPL
 
+config SPL_HANDOFF
+	bool "Pass hand-off information from SPL to U-Boot proper"
+	depends on HANDOFF
+	default y
+	help
+	  This option enables SPL to write handoff information. This can be
+	  used to pass information like the size of SDRAM from SPL to U-Boot
+	  proper. Also SPL can receive information from TPL in the same place
+	  if that is enabled.
+
 config SPL_LDSCRIPT
 	string "Linker script for the SPL stage"
 	default "arch/$(ARCH)/cpu/u-boot-spl.lds"
@@ -867,6 +887,16 @@
 
 if TPL
 
+config TPL_HANDOFF
+	bool "Pass hand-off information from TPL to SPL and U-Boot proper"
+	depends on HANDOFF
+	default y
+	help
+	  This option enables TPL to write handoff information. This can be
+	  used to pass information like the size of SDRAM from TPL to U-Boot
+	  proper. The information is also available to SPL if it is useful
+	  there.
+
 config TPL_BOARD_INIT
 	bool "Call board-specific initialization in TPL"
 	help
diff --git a/common/spl/spl.c b/common/spl/spl.c
index 4313710..35120b6 100644
--- a/common/spl/spl.c
+++ b/common/spl/spl.c
@@ -10,6 +10,7 @@
 #include <bloblist.h>
 #include <binman_sym.h>
 #include <dm.h>
+#include <handoff.h>
 #include <spl.h>
 #include <asm/u-boot.h>
 #include <nand.h>
@@ -45,6 +46,14 @@
  */
 __weak void show_boot_progress(int val) {}
 
+#if defined(CONFIG_SPL_OS_BOOT) || CONFIG_IS_ENABLED(HANDOFF)
+/* weak, default platform-specific function to initialize dram banks */
+__weak int dram_init_banksize(void)
+{
+	return 0;
+}
+#endif
+
 /*
  * Default function to determine if u-boot or the OS should
  * be started. This implementation always returns 1.
@@ -64,14 +73,6 @@
 	return 1;
 }
 
-/* weak default platform specific function to initialize
- * dram banks
- */
-__weak int dram_init_banksize(void)
-{
-	return 0;
-}
-
 /*
  * Weak default function for arch specific zImage check. Return zero
  * and fill start and end address if image is recognized.
@@ -320,6 +321,44 @@
 	image_entry();
 }
 
+#if CONFIG_IS_ENABLED(HANDOFF)
+/**
+ * Set up the SPL hand-off information
+ *
+ * This is initially empty (zero) but can be written by
+ */
+static int setup_spl_handoff(void)
+{
+	struct spl_handoff *ho;
+
+	ho = bloblist_ensure(BLOBLISTT_SPL_HANDOFF, sizeof(struct spl_handoff));
+	if (!ho)
+		return -ENOENT;
+
+	return 0;
+}
+
+static int write_spl_handoff(void)
+{
+	struct spl_handoff *ho;
+
+	ho = bloblist_find(BLOBLISTT_SPL_HANDOFF, sizeof(struct spl_handoff));
+	if (!ho)
+		return -ENOENT;
+	handoff_save_dram(ho);
+#ifdef CONFIG_SANDBOX
+	ho->arch.magic = TEST_HANDOFF_MAGIC;
+#endif
+	debug(SPL_TPL_PROMPT "Wrote SPL handoff\n");
+
+	return 0;
+}
+#else
+static inline int setup_spl_handoff(void) { return 0; }
+static inline int write_spl_handoff(void) { return 0; }
+
+#endif /* HANDOFF */
+
 static int spl_common_init(bool setup_malloc)
 {
 	int ret;
@@ -355,6 +394,15 @@
 			return ret;
 		}
 	}
+	if (CONFIG_IS_ENABLED(HANDOFF)) {
+		int ret;
+
+		ret = setup_spl_handoff();
+		if (ret) {
+			puts(SPL_TPL_PROMPT "Cannot set up SPL handoff\n");
+			hang();
+		}
+	}
 	if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) {
 		ret = fdtdec_setup();
 		if (ret) {
@@ -503,10 +551,6 @@
 
 	spl_set_bd();
 
-#ifdef CONFIG_SPL_OS_BOOT
-	dram_init_banksize();
-#endif
-
 #if defined(CONFIG_SYS_SPL_MALLOC_START)
 	mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
 			CONFIG_SYS_SPL_MALLOC_SIZE);
@@ -528,6 +572,9 @@
 	spl_board_init();
 #endif
 
+	if (IS_ENABLED(CONFIG_SPL_OS_BOOT) || CONFIG_IS_ENABLED(HANDOFF))
+		dram_init_banksize();
+
 	bootcount_inc();
 
 	memset(&spl_image, '\0', sizeof(spl_image));
@@ -544,6 +591,12 @@
 	}
 
 	spl_perform_fixups(&spl_image);
+	if (CONFIG_IS_ENABLED(HANDOFF)) {
+		ret = write_spl_handoff();
+		if (ret)
+			printf(SPL_TPL_PROMPT
+			       "SPL hand-off write failed (err=%d)\n", ret);
+	}
 	if (CONFIG_IS_ENABLED(BLOBLIST)) {
 		ret = bloblist_finish();
 		if (ret)