From 890daca9e2596359808bcffcab31202288bef63f Mon Sep 17 00:00:00 2001 From: Thibaut VARENE Date: Tue, 14 Feb 2017 14:42:10 +0100 Subject: [PATCH] kernel: mtdsplit: Add support for Mikrotik NOR firmware The RouterBOOT bootloader does not care where the kernel lives in the SPI flash, all that matters is that the kernel is wrapped in the custom yaffs container as generated by kernel2minor. This container has a fixed signature as follows: 00000000 00 00 00 01 00 00 00 01 ff ff 6b 65 72 6e 65 6c |..........kernel| This patch adds mtdsplit support for identifying that signature and triggering the search for the rootfs. rootfs is expected at EB boundary since we use wget mtd_find_rootfs_from(). We make no use of the yaffs file size field because it contains invalid data in the image generated by kernel2minor. Signed-off-by: Thibaut VARENE --- target/linux/generic/config-4.4 | 1 + target/linux/generic/config-4.9 | 1 + .../files/drivers/mtd/mtdsplit/Kconfig | 5 + .../files/drivers/mtd/mtdsplit/Makefile | 1 + .../drivers/mtd/mtdsplit/mtdsplit_minor.c | 117 ++++++++++++++++++ 5 files changed, 125 insertions(+) create mode 100644 target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c 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); -- 2.30.2