diff --git a/target/linux/generic/config-4.4 b/target/linux/generic/config-4.4 index ebc54bc7bd..e76359a8ae 100644 --- a/target/linux/generic/config-4.4 +++ b/target/linux/generic/config-4.4 @@ -2367,6 +2367,7 @@ CONFIG_MTD_SPLIT=y CONFIG_MTD_SPLIT_FIRMWARE_NAME="firmware" # CONFIG_MTD_SPLIT_FIT_FW is not set # CONFIG_MTD_SPLIT_LZMA_FW is not set +# CONFIG_MTD_SPLIT_MINOR_FW is not set # CONFIG_MTD_SPLIT_SEAMA_FW is not set CONFIG_MTD_SPLIT_SQUASHFS_ROOT=y CONFIG_MTD_SPLIT_SUPPORT=y diff --git a/target/linux/generic/config-4.9 b/target/linux/generic/config-4.9 index 99ba19f938..e36d77c207 100644 --- a/target/linux/generic/config-4.9 +++ b/target/linux/generic/config-4.9 @@ -2578,6 +2578,7 @@ CONFIG_MTD_SPLIT=y CONFIG_MTD_SPLIT_FIRMWARE_NAME="firmware" # CONFIG_MTD_SPLIT_FIT_FW is not set # CONFIG_MTD_SPLIT_LZMA_FW is not set +# CONFIG_MTD_SPLIT_MINOR_FW is not set # CONFIG_MTD_SPLIT_SEAMA_FW is not set CONFIG_MTD_SPLIT_SQUASHFS_ROOT=y CONFIG_MTD_SPLIT_SUPPORT=y diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig b/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig index 7e653e78a1..4a15d4879b 100644 --- a/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig @@ -64,3 +64,8 @@ config MTD_SPLIT_EVA_FW bool "EVA image based firmware partition parser" depends on MTD_SPLIT_SUPPORT select MTD_SPLIT + +config MTD_SPLIT_MINOR_FW + bool "Mikrotik NOR image based firmware partition parser" + depends on MTD_SPLIT_SUPPORT + select MTD_SPLIT diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile b/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile index c843025ffb..fab85caa75 100644 --- a/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_MTD_SPLIT_TRX_FW) += mtdsplit_trx.o obj-$(CONFIG_MTD_SPLIT_BRNIMAGE_FW) += mtdsplit_brnimage.o obj-$(CONFIG_MTD_SPLIT_EVA_FW) += mtdsplit_eva.o obj-$(CONFIG_MTD_SPLIT_WRGG_FW) += mtdsplit_wrgg.o +obj-$(CONFIG_MTD_SPLIT_MINOR_FW) += mtdsplit_minor.o diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c new file mode 100644 index 0000000000..f971f0a650 --- /dev/null +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c @@ -0,0 +1,117 @@ +/* + * MTD splitter for MikroTik NOR devices + * + * Copyright (C) 2017 Thibaut VARENE + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * The rootfs is expected at erase-block boundary due to the use of + * mtd_find_rootfs_from(). We use a trimmed down version of the yaffs header + * for two main reasons: + * - the original header uses weakly defined types (int, enum...) which can + * vary in length depending on build host (and the struct is not packed), + * and the name field can have a different total length depending on + * whether or not the yaffs code was _built_ with unicode support. + * - the only field that could be of real use here (file_size_low) contains + * invalid data in the header generated by kernel2minor, so we cannot use + * it to infer the exact position of the rootfs and do away with + * mtd_find_rootfs_from() (and thus have non-EB-aligned rootfs). + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mtdsplit.h" + +#define YAFFS_OBJECT_TYPE_FILE 0x1 +#define YAFFS_OBJECTID_ROOT 0x1 +#define YAFFS_SUM_UNUSED 0xFFFF +#define YAFFS_NAME "kernel" + +#define MINOR_NR_PARTS 2 + +/* + * This structure is based on yaffs_obj_hdr from yaffs_guts.h + * The weak types match upstream. The fields have cpu-endianness + */ +struct minor_header { + int yaffs_type; + int yaffs_obj_id; + u16 yaffs_sum_unused; + char yaffs_name[sizeof(YAFFS_NAME)]; +}; + +static int mtdsplit_parse_minor(struct mtd_info *master, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + struct minor_header hdr; + size_t hdr_len, retlen; + size_t rootfs_offset; + struct mtd_partition *parts; + int err; + + hdr_len = sizeof(hdr); + err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr); + if (err) + return err; + + if (retlen != hdr_len) + return -EIO; + + /* match header */ + if (hdr.yaffs_type != YAFFS_OBJECT_TYPE_FILE) + return -EINVAL; + + if (hdr.yaffs_obj_id != YAFFS_OBJECTID_ROOT) + return -EINVAL; + + if (hdr.yaffs_sum_unused != YAFFS_SUM_UNUSED) + return -EINVAL; + + if (memcmp(hdr.yaffs_name, YAFFS_NAME, sizeof(YAFFS_NAME))) + return -EINVAL; + + err = mtd_find_rootfs_from(master, master->erasesize, master->size, + &rootfs_offset, NULL); + if (err) + return err; + + parts = kzalloc(MINOR_NR_PARTS * sizeof(*parts), GFP_KERNEL); + if (!parts) + return -ENOMEM; + + parts[0].name = KERNEL_PART_NAME; + parts[0].offset = 0; + parts[0].size = rootfs_offset; + + parts[1].name = ROOTFS_PART_NAME; + parts[1].offset = rootfs_offset; + parts[1].size = master->size - rootfs_offset; + + *pparts = parts; + return MINOR_NR_PARTS; +} + +static struct mtd_part_parser mtdsplit_minor_parser = { + .owner = THIS_MODULE, + .name = "minor-fw", + .parse_fn = mtdsplit_parse_minor, + .type = MTD_PARSER_TYPE_FIRMWARE, +}; + +static int __init mtdsplit_minor_init(void) +{ + register_mtd_parser(&mtdsplit_minor_parser); + + return 0; +} + +subsys_initcall(mtdsplit_minor_init);