msm: dev_tree: add support for multiple concatenated dev trees

The kernel build can suppor appending multiple device trees
after the zImage. We now iterate through the device trees
looking for a match according to
platform_id/hardware_id/soc_version in the device tree's /qcom,msm_id
node.

Change-Id: I5e6b3da7a9343af2eb1f8ac7890c06f1e2726134
Signed-off-by: Dima Zavin <dima@android.com>
Signed-off-by: Shashank Mittal <mittals@codeaurora.org>
diff --git a/app/aboot/aboot.c b/app/aboot/aboot.c
index d732a4e..e1a4274 100644
--- a/app/aboot/aboot.c
+++ b/app/aboot/aboot.c
@@ -776,7 +776,8 @@
 			 */
 			void *dtb;
 			dtb = dev_tree_appended((void*) hdr->kernel_addr,
-						(void *)hdr->tags_addr, hdr->kernel_size);
+						hdr->kernel_size,
+						(void *)hdr->tags_addr);
 			if (!dtb) {
 				dprintf(CRITICAL, "ERROR: Appended Device Tree Blob not found\n");
 				return -1;
@@ -867,7 +868,8 @@
 			 */
 			void *dtb;
 			dtb = dev_tree_appended((void*) hdr->kernel_addr,
-						(void *)hdr->tags_addr, hdr->kernel_size);
+						kernel_actual,
+						(void *)hdr->tags_addr);
 			if (!dtb) {
 				dprintf(CRITICAL, "ERROR: Appended Device Tree Blob not found\n");
 				return -1;
@@ -1432,7 +1434,8 @@
 	 */
 	if (!dtb_copied) {
 		void *dtb;
-		dtb = dev_tree_appended((void *)hdr->kernel_addr, (void *)hdr->tags_addr, hdr->kernel_size);
+		dtb = dev_tree_appended((void *)hdr->kernel_addr, hdr->kernel_size,
+					(void *)hdr->tags_addr);
 		if (!dtb) {
 			fastboot_fail("dtb not found");
 			return;
diff --git a/platform/msm_shared/dev_tree.c b/platform/msm_shared/dev_tree.c
index ee0582d..28530b8 100644
--- a/platform/msm_shared/dev_tree.c
+++ b/platform/msm_shared/dev_tree.c
@@ -52,6 +52,66 @@
  */
 extern int check_aboot_addr_range_overlap(uint32_t start, uint32_t size);
 
+struct msm_id
+{
+	uint32_t platform_id;
+	uint32_t hardware_id;
+	uint32_t soc_rev;
+};
+
+static bool is_dev_tree_compatible(void *dtb)
+{
+	int root_offset;
+	const void *prop;
+	char model[128];
+	struct msm_id msm_id;
+	int len;
+
+	root_offset = fdt_path_offset(dtb, "/");
+	if (root_offset < 0)
+		return false;
+
+	prop = fdt_getprop(dtb, root_offset, "model", &len);
+	if (prop && len > 0) {
+		memcpy(model, prop, MIN((int)sizeof(model), len));
+		model[sizeof(model) - 1] = '\0';
+	} else {
+		model[0] = '\0';
+	}
+
+	prop = fdt_getprop(dtb, root_offset, "qcom,msm-id", &len);
+	if (!prop || len <= 0) {
+		dprintf(INFO, "qcom,msm-id entry not found\n");
+		return false;
+	} else if (len < (int)sizeof(struct msm_id)) {
+		dprintf(INFO, "qcom,msm-id entry size mismatch (%d != %d)\n",
+			len, sizeof(struct msm_id));
+		return false;
+	}
+	msm_id.platform_id = fdt32_to_cpu(((const struct msm_id *)prop)->platform_id);
+	msm_id.hardware_id = fdt32_to_cpu(((const struct msm_id *)prop)->hardware_id);
+	msm_id.soc_rev = fdt32_to_cpu(((const struct msm_id *)prop)->soc_rev);
+
+	dprintf(INFO, "Found an appended flattened device tree (%s - %d %d 0x%x)\n",
+		*model ? model : "unknown",
+		msm_id.platform_id, msm_id.hardware_id, msm_id.soc_rev);
+
+	if (msm_id.platform_id != board_platform_id() ||
+	    msm_id.hardware_id != board_hardware_id() ||
+	    msm_id.soc_rev != board_soc_version()) {
+		dprintf(INFO, "Device tree's msm_id doesn't match the board: <%d %d 0x%x> != <%d %d 0x%x>\n",
+			msm_id.platform_id,
+			msm_id.hardware_id,
+			msm_id.soc_rev,
+			board_platform_id(),
+			board_hardware_id(),
+			board_soc_version());
+		return false;
+	}
+
+	return true;
+}
+
 /*
  * Will relocate the DTB to the tags addr if the device tree is found and return
  * its address
@@ -63,46 +123,44 @@
  * Return Value: DTB address : If appended device tree is found
  *               'NULL'         : Otherwise
  */
-void *dev_tree_appended(void *kernel, void *tags, uint32_t kernel_size)
+void *dev_tree_appended(void *kernel, uint32_t kernel_size, void *tags)
 {
+	void *kernel_end = kernel + kernel_size;
 	uint32_t app_dtb_offset = 0;
-	uint32_t size;
+	void *dtb;
 
 	memcpy((void*) &app_dtb_offset, (void*) (kernel + DTB_OFFSET), sizeof(uint32_t));
 
-	/*
-	 * Check if we have valid offset for the DTB, if not return error.
-	 * If the kernel image does not have appeneded device tree, DTB offset
-	 * might contain some random address which is not accessible & cause
-	 * data abort. If kernel start + dtb offset address exceed the total
-	 * size of the kernel, then we dont have an appeneded DTB.
-	 */
-	if (app_dtb_offset < kernel_size)
-	{
-		if (!fdt_check_header((void*) (kernel + app_dtb_offset)))
-		{
-			void *dtb;
-			int rc;
+	dtb = kernel + app_dtb_offset;
+	while (dtb + sizeof(struct fdt_header) < kernel_end) {
+		bool compat;
+		struct fdt_header dtb_hdr;
+		uint32_t dtb_size;
 
-			dprintf(INFO, "Found Appeneded Flattened Device tree\n");
-			dtb = kernel + app_dtb_offset;
-			size = fdt_totalsize(dtb);
-			if (check_aboot_addr_range_overlap(tags, size))
-			{
-				dprintf(CRITICAL, "Appended dtb aboot overlap check failed.\n");
-				return NULL;
-			}
-			rc = fdt_open_into(dtb, tags, size);
-			if (rc == 0)
-			{
-				/* clear out the old DTB magic so kernel doesn't find it */
-				*((uint32_t *)dtb) = 0;
-				return tags;
-			}
+		/* the DTB could be unaligned, so extract the header,
+		 * and operate on it separately */
+		memcpy(&dtb_hdr, dtb, sizeof(struct fdt_header));
+		if (fdt_check_header((const void *)&dtb_hdr) != 0 ||
+		    (dtb + fdt_totalsize((const void *)&dtb_hdr) > kernel_end))
+			break;
+		dtb_size = fdt_totalsize(&dtb_hdr);
+
+		/* now that we know we have a valid DTB, we need to copy
+		 * it somewhere aligned, like tags */
+		memcpy(tags, dtb, dtb_size);
+
+		compat = is_dev_tree_compatible(tags);
+		if (compat) {
+			/* clear out the old DTB magic so kernel doesn't find it */
+			*((uint32_t *)(kernel + app_dtb_offset)) = 0;
+			return tags;
 		}
+
+		/* goto the next device tree if any */
+		dtb += dtb_size;
 	}
-	else
-		dprintf(CRITICAL, "DTB offset is incorrect, kernel image does not have appended DTB\n");
+
+	dprintf(CRITICAL, "DTB offset is incorrect, kernel image does not have appended DTB\n");
 
 	return NULL;
 }
diff --git a/platform/msm_shared/include/dev_tree.h b/platform/msm_shared/include/dev_tree.h
index d0b0ca8..2a8ee01 100644
--- a/platform/msm_shared/include/dev_tree.h
+++ b/platform/msm_shared/include/dev_tree.h
@@ -70,5 +70,5 @@
 int dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info);
 int update_device_tree(void *, const char *, void *, unsigned);
 int dev_tree_add_mem_info(void *fdt, uint32_t offset, uint32_t size, uint32_t addr);
-void *dev_tree_appended(void *kernel, void *tags, uint32_t kernel_size);
+void *dev_tree_appended(void *kernel, uint32_t kernel_size, void *tags);
 #endif