From 2a295753a10823a47542c779a25bbb1f52c71281 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Fri, 3 Aug 2012 10:27:13 +0200
Subject: [PATCH 19/25] owrt mtd split

---
 .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h |    1 +
 arch/mips/lantiq/setup.c                           |    7 +
 drivers/mtd/Kconfig                                |    4 +
 drivers/mtd/mtdpart.c                              |  173 +++++++++++++++++++-
 4 files changed, 184 insertions(+), 1 deletions(-)

diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h
index 1ec8f2a..1ff93cc 100644
--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h
+++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h
@@ -166,6 +166,7 @@ extern unsigned char ltq_boot_select(void);
 
 extern __iomem void *ltq_ebu_membase;
 extern __iomem void *ltq_cgu_membase;
+extern unsigned long ltq_brn_boot;
 
 static inline int ltq_is_ase(void)
 {
diff --git a/arch/mips/lantiq/setup.c b/arch/mips/lantiq/setup.c
index 1ff6c9d..708e969 100644
--- a/arch/mips/lantiq/setup.c
+++ b/arch/mips/lantiq/setup.c
@@ -18,6 +18,9 @@
 #include "devices.h"
 #include "prom.h"
 
+/* set to 1 if the bootloader is BRN-BOOT instead of u-boot */
+unsigned long ltq_brn_boot = 0;
+
 void __init plat_mem_setup(void)
 {
 	/* assume 16M as default incase uboot fails to pass proper ramsize */
@@ -38,6 +41,10 @@ void __init plat_mem_setup(void)
 			if (strict_strtoul(e, 0, &memsize))
 				pr_warn("bad memsize specified\n");
 		}
+		if (!strncmp(e, "BRN-BOOT", 8)){
+			pr_info("Found BRN-BOOT instead of u-boot\n");
+			ltq_brn_boot = 1;
+		}
 		envp++;
 	}
 	memsize *= 1024 * 1024;
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 982a98b..e2f3f3e 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -31,6 +31,10 @@ config MTD_ROOTFS_SPLIT
 	bool "Automatically split 'rootfs' partition for squashfs"
 	default y
 
+config MTD_UIMAGE_SPLIT
+	bool "Automatically split 'linux' partition into 'kernel' and 'rootfs'"
+	default y
+
 config MTD_REDBOOT_PARTS
 	tristate "RedBoot partition table parsing"
 	---help---
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 855b70b..93711e2 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -867,6 +867,168 @@ static int refresh_rootfs_split(struct mtd_info *mtd)
 }
 #endif /* CONFIG_MTD_ROOTFS_SPLIT */
 
+#ifdef CONFIG_MTD_UIMAGE_SPLIT
+static unsigned long find_uimage_size(struct mtd_info *mtd,
+				      unsigned long offset)
+{
+#define UBOOT_MAGIC	0x56190527
+	unsigned long magic = 0;
+	unsigned long temp;
+	size_t len;
+	int ret;
+
+	ret = mtd->read(mtd, offset, 4, &len, (void *)&magic);
+	if (ret || len != sizeof(magic))
+		return 0;
+
+	if (le32_to_cpu(magic) != UBOOT_MAGIC)
+		return 0;
+
+	ret = mtd->read(mtd, offset + 12, 4, &len, (void *)&temp);
+	if (ret || len != sizeof(temp))
+		return 0;
+
+	return temp + 0x40;
+}
+
+static unsigned long find_eva_size(struct mtd_info *mtd,
+				      unsigned long offset)
+{
+#define EVA_MAGIC	0xfeed1281
+	unsigned long magic = 0;
+	unsigned long temp;
+	size_t len;
+	int ret;
+
+	ret = mtd->read(mtd, offset, 4, &len, (void *)&magic);
+	if (ret || len != sizeof(magic))
+		return 0;
+
+	if (le32_to_cpu(magic) != EVA_MAGIC)
+		return 0;
+
+	ret = mtd->read(mtd, offset + 4, 4, &len, (void *)&temp);
+	if (ret || len != sizeof(temp))
+		return 0;
+
+	/* add eva header size */
+	temp = le32_to_cpu(temp) + 0x18;
+
+	temp &= ~0xffff;
+	temp += 0x10000;
+	return temp;
+}
+
+static int detect_squashfs_partition(struct mtd_info *mtd, unsigned long offset)
+{
+	unsigned long temp;
+	size_t len;
+	int ret;
+
+	ret = mtd->read(mtd, offset, 4, &len, (void *)&temp);
+	if (ret || len != sizeof(temp))
+		return 0;
+
+
+	return le32_to_cpu(temp) == SQUASHFS_MAGIC;
+}
+
+static int detect_eva_squashfs_partition(struct mtd_info *mtd, unsigned long offset)
+{
+	unsigned long temp;
+	size_t len;
+	int ret;
+
+	ret = mtd->read(mtd, offset, 4, &len, (void *)&temp);
+	if (ret || len != sizeof(temp))
+		return 0;
+
+	return be32_to_cpu(temp) == SQUASHFS_MAGIC;
+}
+
+static unsigned long find_brnimage_size(struct mtd_info *mtd,
+					unsigned long offset)
+{
+	unsigned long buf[4];
+	// Assume at most 2MB of kernel image
+	unsigned long end = offset + (2 << 20);
+	unsigned long ptr = offset + 0x400 - 12;
+	size_t len;
+	int ret;
+
+	while (ptr < end) {
+		long size_min = ptr - 0x400 - 12 - offset;
+		long size_max = ptr + 12 - offset;
+		ret = mtd->read(mtd, ptr, 16, &len, (void *)buf);
+		if (ret || len != 16)
+			return 0;
+
+		if (le32_to_cpu(buf[0]) < size_min ||
+		    le32_to_cpu(buf[0]) > size_max) {
+			ptr += 0x400;
+			continue;
+		}
+
+		if (le32_to_cpu(buf[3]) == SQUASHFS_MAGIC)
+			return ptr + 12 - offset;
+
+		ptr += 0x400;
+	}
+
+	return 0;
+}
+
+static int split_uimage(struct mtd_info *mtd,
+			const struct mtd_partition *part)
+{
+	static struct mtd_partition split_partitions[] = {
+		{
+			.name = "kernel",
+			.offset = 0x0,
+			.size = 0x0,
+		}, {
+			.name = "rootfs",
+			.offset = 0x0,
+			.size = 0x0,
+		},
+	};
+
+	split_partitions[0].size = find_uimage_size(mtd, part->offset);
+	if (!split_partitions[0].size) {
+		split_partitions[0].size = find_eva_size(mtd, part->offset);
+		if (!split_partitions[0].size) {
+			split_partitions[0].size = find_brnimage_size(mtd, part->offset);
+			if (!split_partitions[0].size) {
+				printk(KERN_NOTICE "no uImage or brnImage or eva found in linux partition\n");
+				return -1;
+			}
+		}
+	}
+
+	if (detect_eva_squashfs_partition(mtd,
+				       part->offset
+				       + split_partitions[0].size)) {
+		split_partitions[0].size += 0x100;
+		pr_info("found eva dummy squashfs behind kernel\n");
+	} else if (!detect_squashfs_partition(mtd,
+				       part->offset
+				       + split_partitions[0].size)) {
+		split_partitions[0].size &= ~(mtd->erasesize - 1);
+		split_partitions[0].size += mtd->erasesize;
+	} else {
+		pr_info("found squashfs behind kernel\n");
+	}
+
+	split_partitions[0].offset = part->offset;
+	split_partitions[1].offset = part->offset + split_partitions[0].size;
+	split_partitions[1].size = part->size - split_partitions[0].size;
+
+	add_mtd_partitions(mtd, split_partitions, 2);
+
+	return 0;
+}
+#endif
+
 /*
  * This function, given a master MTD object and a partition table, creates
  * and registers slave MTD objects which are bound to the master according to
@@ -883,7 +1045,7 @@ int add_mtd_partitions(struct mtd_info *master,
 	struct mtd_part *slave;
 	uint64_t cur_offset = 0;
 	int i;
-#ifdef CONFIG_MTD_ROOTFS_SPLIT
+#if defined(CONFIG_MTD_ROOTFS_SPLIT) || defined(CONFIG_MTD_UIMAGE_SPLIT)
 	int ret;
 #endif
 
@@ -900,6 +1062,15 @@ int add_mtd_partitions(struct mtd_info *master,
 
 		add_mtd_device(&slave->mtd);
 
+#ifdef CONFIG_MTD_UIMAGE_SPLIT
+		if (!strcmp(parts[i].name, "linux")) {
+			ret = split_uimage(master, &parts[i]);
+
+			if (ret)
+				printk(KERN_WARNING "Can't split linux partition\n");
+		}
+#endif
+
 		if (!strcmp(parts[i].name, "rootfs")) {
 #ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
 			if (ROOT_DEV == 0) {
-- 
1.7.9.1