arm: Add support for semihosting for armv8 fastmodel targets.

The armv8 ARM Trusted Firmware (ATF) can be used to load various ATF
images and u-boot, and does this for virtual platforms by using
semihosting. This commit extends this idea by allowing u-boot to also
use semihosting to load the kernel/ramdisk/dtb. This eliminates the need
for a bootwrapper and produces a more realistic boot sequence with
virtual models.

Though the semihosting code is quite generic, support for armv7 in
fastmodel is less useful due to the wide range of available silicon
and the lack of a free armv7 fastmodel, so this change contains an
untested armv7 placeholder for the service trap opcode.

Please refer to doc/README.semihosting for a more detailed description
of semihosting and how it is used with the armv8 virtual platforms.

Signed-off-by: Darwin Rambo <drambo@broadcom.com>
Cc: trini@ti.com
Cc: fenghua@phytium.com.cn
Cc: bhupesh.sharma@freescale.com
diff --git a/board/armltd/vexpress64/vexpress64.c b/board/armltd/vexpress64/vexpress64.c
index 2ec3bc9..5897318 100644
--- a/board/armltd/vexpress64/vexpress64.c
+++ b/board/armltd/vexpress64/vexpress64.c
@@ -11,6 +11,7 @@
 #include <netdev.h>
 #include <asm/io.h>
 #include <linux/compiler.h>
+#include <asm/semihosting.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -31,11 +32,6 @@
 	return 0;
 }
 
-int timer_init(void)
-{
-	return 0;
-}
-
 /*
  * Board specific reset that is system reset.
  */
@@ -43,6 +39,101 @@
 {
 }
 
+#ifdef CONFIG_BOARD_LATE_INIT
+int board_late_init(void)
+{
+#ifdef CONFIG_SEMIHOSTING
+	/*
+	 * Please refer to doc/README.semihosting for a more complete
+	 * description.
+	 *
+	 * We require that the board include file defines these env variables:
+	 * - kernel_name
+	 * - kernel_addr_r
+	 * - initrd_name
+	 * - initrd_addr_r
+	 * - fdt_name
+	 * - fdt_addr_r
+	 *
+	 * For the "fdt chosen" startup macro, this code will then define:
+	 * - initrd_end (based on initrd_addr_r plus actual initrd_size)
+	 *
+	 * We will then load the kernel, initrd, and fdt into the specified
+	 * locations in memory in a similar way that the ATF fastmodel code
+	 * uses semihosting calls to load other boot stages and u-boot itself.
+	 */
+
+	/* Env variable strings */
+	char *kernel_name = getenv("kernel_name");
+	char *kernel_addr_str = getenv("kernel_addr_r");
+	char *initrd_name = getenv("initrd_name");
+	char *initrd_addr_str = getenv("initrd_addr_r");
+	char *fdt_name = getenv("fdt_name");
+	char *fdt_addr_str = getenv("fdt_addr_r");
+	char initrd_end_str[64];
+
+	/* Actual addresses converted from env variables */
+	void *kernel_addr_r;
+	void *initrd_addr_r;
+	void *fdt_addr_r;
+
+	/* Actual initrd base and size */
+	unsigned long initrd_base;
+	unsigned long initrd_size;
+
+	/* Space available */
+	int avail;
+
+	/* Make sure the environment variables needed are set */
+	if (!(kernel_addr_str && initrd_addr_str && fdt_addr_str)) {
+		printf("%s: Define {kernel/initrd/fdt}_addr_r\n", __func__);
+		return -1;
+	}
+	if (!(kernel_name && initrd_name && fdt_name)) {
+		printf("%s: Define {kernel/initrd/fdt}_name\n", __func__);
+		return -1;
+	}
+
+	/* Get exact initrd_size */
+	initrd_size = smh_len(initrd_name);
+	if (initrd_size == -1) {
+		printf("%s: Can't get file size for \'%s\'\n", __func__,
+		       initrd_name);
+		return -1;
+	}
+
+	/* Set initrd_end */
+	initrd_base = simple_strtoul(initrd_addr_str, NULL, 16);
+	initrd_addr_r = (void *)initrd_base;
+	sprintf(initrd_end_str, "0x%lx", initrd_base + initrd_size - 1);
+	setenv("initrd_end", initrd_end_str);
+
+	/* Load kernel to memory */
+	fdt_addr_r = (void *)simple_strtoul(fdt_addr_str, NULL, 16);
+	kernel_addr_r = (void *)simple_strtoul(kernel_addr_str, NULL, 16);
+
+	/*
+	 * The kernel must be lower in memory than fdt and loading the
+	 * kernel must not trample the fdt or vice versa.
+	 */
+	avail = fdt_addr_r - kernel_addr_r;
+	if (avail < 0) {
+		printf("%s: fdt must be after kernel\n", __func__);
+		return -1;
+	}
+	smh_load(kernel_name, kernel_addr_r, avail, 1);
+
+	/* Load fdt to memory */
+	smh_load(fdt_name, fdt_addr_r, 0x20000, 1);
+
+	/* Load initrd to memory */
+	smh_load(initrd_name, initrd_addr_r, initrd_size, 1);
+
+#endif				/* CONFIG_SEMIHOSTING */
+	return 0;
+}
+#endif				/* CONFIG_BOARD_LATE_INIT */
+
 /*
  * Board specific ethernet initialization routine.
  */