fit: Introduce methods for applying overlays on fit-load

Introduce an overlay based method for constructing a base DT blob
to pass to the kernel.

It is based on a specific method now to get the FDT from a FIT image
named boot_get_fdt_fit().

Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
Acked-by: Simon Glass <sjg@chromium.org>
diff --git a/common/image-fdt.c b/common/image-fdt.c
index da4d007..a2ef409 100644
--- a/common/image-fdt.c
+++ b/common/image-fdt.c
@@ -356,17 +356,16 @@
 			if (fit_check_format(buf)) {
 				ulong load, len;
 
-				fdt_noffset = fit_image_load(images,
+				fdt_noffset = boot_get_fdt_fit(images,
 					fdt_addr, &fit_uname_fdt,
 					&fit_uname_config,
-					arch, IH_TYPE_FLATDT,
-					BOOTSTAGE_ID_FIT_FDT_START,
-					FIT_LOAD_OPTIONAL, &load, &len);
+					arch, &load, &len);
 
 				images->fit_hdr_fdt = map_sysmem(fdt_addr, 0);
 				images->fit_uname_fdt = fit_uname_fdt;
 				images->fit_noffset_fdt = fdt_noffset;
 				fdt_addr = load;
+
 				break;
 			} else
 #endif
diff --git a/common/image-fit.c b/common/image-fit.c
index e75cb64..7f17fd1 100644
--- a/common/image-fit.c
+++ b/common/image-fit.c
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include <mapmem.h>
 #include <asm/io.h>
+#include <malloc.h>
 DECLARE_GLOBAL_DATA_PTR;
 #endif /* !USE_HOSTCC*/
 
@@ -434,6 +435,10 @@
 			printf("0x%08lx\n", load);
 	}
 
+	/* optional load address for FDT */
+	if (type == IH_TYPE_FLATDT && !fit_image_get_load(fit, image_noffset, &load))
+		printf("%s  Load Address: 0x%08lx\n", p, load);
+
 	if ((type == IH_TYPE_KERNEL) || (type == IH_TYPE_STANDALONE) ||
 	    (type == IH_TYPE_RAMDISK)) {
 		ret = fit_image_get_entry(fit, image_noffset, &entry);
@@ -1454,6 +1459,8 @@
 {
 	int noffset, confs_noffset;
 	int len;
+	const char *s;
+	char *conf_uname_copy = NULL;
 
 	confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH);
 	if (confs_noffset < 0) {
@@ -1475,12 +1482,29 @@
 		debug("Found default configuration: '%s'\n", conf_uname);
 	}
 
+	s = strchr(conf_uname, '#');
+	if (s) {
+		len = s - conf_uname;
+		conf_uname_copy = malloc(len + 1);
+		if (!conf_uname_copy) {
+			debug("Can't allocate uname copy: '%s'\n",
+					conf_uname);
+			return -ENOMEM;
+		}
+		memcpy(conf_uname_copy, conf_uname, len);
+		conf_uname_copy[len] = '\0';
+		conf_uname = conf_uname_copy;
+	}
+
 	noffset = fdt_subnode_offset(fit, confs_noffset, conf_uname);
 	if (noffset < 0) {
 		debug("Can't get node offset for configuration unit name: '%s' (%s)\n",
 		      conf_uname, fdt_strerror(noffset));
 	}
 
+	if (conf_uname_copy)
+		free(conf_uname_copy);
+
 	return noffset;
 }
 
@@ -1527,7 +1551,7 @@
 	char *desc;
 	const char *uname;
 	int ret;
-	int loadables_index;
+	int fdt_index, loadables_index;
 
 	/* Mandatory properties */
 	ret = fit_get_desc(fit, noffset, &desc);
@@ -1549,9 +1573,17 @@
 	if (uname)
 		printf("%s  Init Ramdisk: %s\n", p, uname);
 
-	uname = fdt_getprop(fit, noffset, FIT_FDT_PROP, NULL);
-	if (uname)
-		printf("%s  FDT:          %s\n", p, uname);
+	for (fdt_index = 0;
+	     uname = fdt_stringlist_get(fit, noffset, FIT_FDT_PROP,
+					fdt_index, NULL), uname;
+	     fdt_index++) {
+
+		if (fdt_index == 0)
+			printf("%s  FDT:          ", p);
+		else
+			printf("%s                ", p);
+		printf("%s\n", uname);
+	}
 
 	uname = fdt_getprop(fit, noffset, FIT_FPGA_PROP, NULL);
 	if (uname)
@@ -1888,3 +1920,144 @@
 
 	return ret;
 }
+
+#ifndef USE_HOSTCC
+int boot_get_fdt_fit(bootm_headers_t *images, ulong addr,
+		   const char **fit_unamep, const char **fit_uname_configp,
+		   int arch, ulong *datap, ulong *lenp)
+{
+	int fdt_noffset, cfg_noffset, count;
+	const void *fit;
+	const char *fit_uname = NULL;
+	const char *fit_uname_config = NULL;
+	char *fit_uname_config_copy = NULL;
+	char *next_config = NULL;
+	ulong load, len;
+#ifdef CONFIG_OF_LIBFDT_OVERLAY
+	ulong image_start, image_end;
+	ulong ovload, ovlen;
+	const char *uconfig;
+	const char *uname;
+	void *base, *ov;
+	int i, err, noffset, ov_noffset;
+#endif
+
+	fit_uname = fit_unamep ? *fit_unamep : NULL;
+
+	if (fit_uname_configp && *fit_uname_configp) {
+		fit_uname_config_copy = strdup(*fit_uname_configp);
+		if (!fit_uname_config_copy)
+			return -ENOMEM;
+
+		next_config = strchr(fit_uname_config_copy, '#');
+		if (next_config)
+			*next_config++ = '\0';
+		if (next_config - 1 > fit_uname_config_copy)
+			fit_uname_config = fit_uname_config_copy;
+	}
+
+	fdt_noffset = fit_image_load(images,
+		addr, &fit_uname, &fit_uname_config,
+		arch, IH_TYPE_FLATDT,
+		BOOTSTAGE_ID_FIT_FDT_START,
+		FIT_LOAD_OPTIONAL, &load, &len);
+
+	if (fdt_noffset < 0)
+		goto out;
+
+	debug("fit_uname=%s, fit_uname_config=%s\n",
+			fit_uname ? fit_uname : "<NULL>",
+			fit_uname_config ? fit_uname_config : "<NULL>");
+
+	fit = map_sysmem(addr, 0);
+
+	cfg_noffset = fit_conf_get_node(fit, fit_uname_config);
+
+	/* single blob, or error just return as well */
+	count = fit_conf_get_prop_node_count(fit, cfg_noffset, FIT_FDT_PROP);
+	if (count <= 1 && !next_config)
+		goto out;
+
+	/* we need to apply overlays */
+
+#ifdef CONFIG_OF_LIBFDT_OVERLAY
+	image_start = addr;
+	image_end = addr + fit_get_size(fit);
+	/* verify that relocation took place by load address not being in fit */
+	if (load >= image_start && load < image_end) {
+		/* check is simplified; fit load checks for overlaps */
+		printf("Overlayed FDT requires relocation\n");
+		fdt_noffset = -EBADF;
+		goto out;
+	}
+
+	base = map_sysmem(load, len);
+
+	/* apply extra configs in FIT first, followed by args */
+	for (i = 1; ; i++) {
+		if (i < count) {
+			noffset = fit_conf_get_prop_node_index(fit, cfg_noffset,
+							       FIT_FDT_PROP, i);
+			uname = fit_get_name(fit, noffset, NULL);
+			uconfig = NULL;
+		} else {
+			if (!next_config)
+				break;
+			uconfig = next_config;
+			next_config = strchr(next_config, '#');
+			if (next_config)
+				*next_config++ = '\0';
+			uname = NULL;
+		}
+
+		debug("%d: using uname=%s uconfig=%s\n", i, uname, uconfig);
+
+		ov_noffset = fit_image_load(images,
+			addr, &uname, &uconfig,
+			arch, IH_TYPE_FLATDT,
+			BOOTSTAGE_ID_FIT_FDT_START,
+			FIT_LOAD_REQUIRED, &ovload, &ovlen);
+		if (ov_noffset < 0) {
+			printf("load of %s failed\n", uname);
+			continue;
+		}
+		debug("%s loaded at 0x%08lx len=0x%08lx\n",
+				uname, ovload, ovlen);
+		ov = map_sysmem(ovload, ovlen);
+
+		base = map_sysmem(load, len + ovlen);
+		err = fdt_open_into(base, base, len + ovlen);
+		if (err < 0) {
+			printf("failed on fdt_open_into\n");
+			fdt_noffset = err;
+			goto out;
+		}
+		/* the verbose method prints out messages on error */
+		err = fdt_overlay_apply_verbose(base, ov);
+		if (err < 0) {
+			fdt_noffset = err;
+			goto out;
+		}
+		fdt_pack(base);
+		len = fdt_totalsize(base);
+	}
+#else
+	printf("config with overlays but CONFIG_OF_LIBFDT_OVERLAY not set\n");
+	fdt_noffset = -EBADF;
+#endif
+
+out:
+	if (datap)
+		*datap = load;
+	if (lenp)
+		*lenp = len;
+	if (fit_unamep)
+		*fit_unamep = fit_uname;
+	if (fit_uname_configp)
+		*fit_uname_configp = fit_uname_config;
+
+	if (fit_uname_config_copy)
+		free(fit_uname_config_copy);
+	return fdt_noffset;
+}
+#endif
diff --git a/include/image.h b/include/image.h
index 339f79c..af98ed9 100644
--- a/include/image.h
+++ b/include/image.h
@@ -594,6 +594,31 @@
 		       ulong *setup_start, ulong *setup_len);
 
 /**
+ * boot_get_fdt_fit() - load a DTB from a FIT file (applying overlays)
+ *
+ * This deals with all aspects of loading an DTB from a FIT.
+ * The correct base image based on configuration will be selected, and
+ * then any overlays specified will be applied (as present in fit_uname_configp).
+ *
+ * @param images	Boot images structure
+ * @param addr		Address of FIT in memory
+ * @param fit_unamep	On entry this is the requested image name
+ *			(e.g. "kernel@1") or NULL to use the default. On exit
+ *			points to the selected image name
+ * @param fit_uname_configp	On entry this is the requested configuration
+ *			name (e.g. "conf@1") or NULL to use the default. On
+ *			exit points to the selected configuration name.
+ * @param arch		Expected architecture (IH_ARCH_...)
+ * @param datap		Returns address of loaded image
+ * @param lenp		Returns length of loaded image
+ *
+ * @return node offset of base image, or -ve error code on error
+ */
+int boot_get_fdt_fit(bootm_headers_t *images, ulong addr,
+		   const char **fit_unamep, const char **fit_uname_configp,
+		   int arch, ulong *datap, ulong *lenp);
+
+/**
  * fit_image_load() - load an image from a FIT
  *
  * This deals with all aspects of loading an image from a FIT, including