--- a/arch/mips/lantiq/xway/Makefile +++ b/arch/mips/lantiq/xway/Makefile @@ -1,6 +1,6 @@ obj-y := prom.o sysctrl.o clk.o reset.o dma.o timer.o dcdc.o -obj-y += vmmc.o tffs.o +obj-y += vmmc.o tffs.o mtd_split.o obj-y += eth_mac.o obj-$(CONFIG_PCI) += ath_eep.o rt_eep.o pci-ath-fixup.o --- /dev/null +++ b/arch/mips/lantiq/xway/mtd_split.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +#define ROOTFS_SPLIT_NAME "rootfs_data" + +struct squashfs_super_block { + __le32 s_magic; + __le32 pad0[9]; + __le64 bytes_used; +}; + +static void split_brnimage_kernel(struct mtd_info *master, const char *name, + int offset, int size) +{ + unsigned long buf[4]; + // Assume at most 2MB of kernel image + unsigned long end = offset + (2 << 20); + unsigned long part_size = offset + 0x400 - 12; + size_t len; + int ret; + + if (strcmp(name, "firmware") != 0) + return; + while (part_size < end) { + long size_min = part_size - 0x400 - 12 - offset; + long size_max = part_size + 12 - offset; + ret = mtd_read(master, part_size, 16, &len, (void *)buf); + if (ret || len != 16) + return; + + if (le32_to_cpu(buf[0]) < size_min || + le32_to_cpu(buf[0]) > size_max) { + part_size += 0x400; + continue; + } + + if (le32_to_cpu(buf[3]) == SQUASHFS_MAGIC) { + part_size += 12 - offset; + __mtd_add_partition(master, "rootfs", offset + part_size, + size - part_size, false); + return; + } + part_size += 0x400; + } +} + +static void split_eva_kernel(struct mtd_info *master, const char *name, + int offset, int size) +{ +#define EVA_MAGIC 0xfeed1281 + unsigned long magic = 0; + unsigned long part_size = 0, p; + size_t len; + int ret; + + if (strcmp(name, CONFIG_MTD_SPLIT_FIRMWARE_NAME) != 0) + return; + + ret = mtd_read(master, offset, 4, &len, (void *)&magic); + if (ret || len != sizeof(magic)) + return; + + if (le32_to_cpu(magic) != EVA_MAGIC) + return; + + ret = mtd_read(master, offset + 4, 4, &len, (void *)&part_size); + if (ret || len != sizeof(part_size)) + return; + + p = part_size = le32_to_cpu(part_size) + 0x18; + p &= ~0xffff; + p += 0x10000; + + ret = mtd_read(master, offset + p, 4, &len, (void *)&magic); + if (ret || len != sizeof(magic)) + return; + + if (magic == SQUASHFS_MAGIC) + part_size = p + 0x100; + else + part_size = mtd_pad_erasesize(master, offset, len); + + if (part_size + master->erasesize > size) + return; + + __mtd_add_partition(master, "rootfs", offset + part_size, + size - part_size, false); +} + +static void split_tplink_kernel(struct mtd_info *master, const char *name, + int offset, int size) +{ +#define TPLINK_MAGIC 0x00000002 + unsigned long magic = 0; + unsigned long part_size = 0; + size_t len; + int ret; + + if (strcmp(name, CONFIG_MTD_SPLIT_FIRMWARE_NAME) != 0) + return; + + ret = mtd_read(master, offset, 4, &len, (void *)&magic); + if (ret || len != sizeof(magic)) + return; + + if (le32_to_cpu(magic) != TPLINK_MAGIC) + return; + + ret = mtd_read(master, offset + 0x78, 4, &len, (void *)&part_size); + if (ret || len != sizeof(part_size)) + return; + + part_size = be32_to_cpu(part_size) + 0x200; + if (part_size + master->erasesize > size) + return; + + __mtd_add_partition(master, "rootfs", offset + part_size, + size - part_size, false); +} + +void arch_split_mtd_part(struct mtd_info *master, const char *name, + int offset, int size) +{ + split_tplink_kernel(master, name, offset, size); + split_eva_kernel(master, name, offset, size); + split_brnimage_kernel(master, name, offset, size); +} --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -89,12 +89,17 @@ extern void deregister_mtd_parser(struct int mtd_is_partition(const struct mtd_info *mtd); int mtd_add_partition(struct mtd_info *master, const char *name, long long offset, long long length); +int __mtd_add_partition(struct mtd_info *master, const char *name, + long long offset, long long length, bool dup_check); + int mtd_del_partition(struct mtd_info *master, int partno); struct mtd_info *mtdpart_get_master(const struct mtd_info *mtd); uint64_t mtdpart_get_offset(const struct mtd_info *mtd); uint64_t mtd_get_device_size(const struct mtd_info *mtd); -extern void __weak arch_split_mtd_part(struct mtd_info *master, - const char *name, int offset, int size); +void __weak arch_split_mtd_part(struct mtd_info *master, + const char *name, int offset, int size); +unsigned long +mtd_pad_erasesize(struct mtd_info *mtd, int offset, int len); int parse_mtd_partitions_by_type(struct mtd_info *master, enum mtd_parser_type type, --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -628,7 +628,7 @@ out_register: } -static int +int __mtd_add_partition(struct mtd_info *master, const char *name, long long offset, long long length, bool dup_check) { @@ -749,7 +749,7 @@ run_parsers_by_type(struct mtd_part *sla return nr_parts; } -static inline unsigned long +unsigned long mtd_pad_erasesize(struct mtd_info *mtd, int offset, int len) { unsigned long mask = mtd->erasesize - 1; @@ -819,7 +819,6 @@ static void split_uimage(struct mtd_info return; len = be32_to_cpu(hdr.size) + 0x40; - len = mtd_pad_erasesize(master, part->offset, len); if (len + master->erasesize > part->mtd.size) return;