diff options
author | Pawel Dembicki <paweldembicki@gmail.com> | 2018-02-04 13:11:16 +0100 |
---|---|---|
committer | John Crispin <john@phrozen.org> | 2018-02-11 16:02:52 +0100 |
commit | 680e867d7f48bc326007e0b2bd8abba1ae702da6 (patch) | |
tree | c268d6f582ba7381111fe34dc173a885e09b2867 /target/linux/generic/files | |
parent | 1113fcdc800a19e502a0558d8cf0e508f4ec6a1e (diff) | |
download | upstream-680e867d7f48bc326007e0b2bd8abba1ae702da6.tar.gz upstream-680e867d7f48bc326007e0b2bd8abba1ae702da6.tar.bz2 upstream-680e867d7f48bc326007e0b2bd8abba1ae702da6.zip |
kernel: mtdsplit: Add support for D-link JBOOT
The D-Link devices with JBOOT bootloader use their own kernel
image header (stag + sch2 headers).
This driver find jImage header and set rootfs start after kernel file.
Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
Diffstat (limited to 'target/linux/generic/files')
3 files changed, 283 insertions, 0 deletions
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig b/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig index 4a15d4879b..81ece43db8 100644 --- a/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig @@ -69,3 +69,8 @@ config MTD_SPLIT_MINOR_FW bool "Mikrotik NOR image based firmware partition parser" depends on MTD_SPLIT_SUPPORT select MTD_SPLIT + +config MTD_SPLIT_JIMAGE_FW + bool "JBOOT 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 fab85caa75..206e754a18 100644 --- a/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile @@ -10,3 +10,4 @@ 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 +obj-$(CONFIG_MTD_SPLIT_JIMAGE_FW) += mtdsplit_jimage.o diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_jimage.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_jimage.c new file mode 100644 index 0000000000..51544a794b --- /dev/null +++ b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_jimage.c @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2018 Paweł Dembicki <paweldembicki@gmail.com> + * + * Based on: mtdsplit_uimage.c + * Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org> + * + * 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/byteorder/generic.h> + +#include "mtdsplit.h" + +#define MAX_HEADER_LEN ( STAG_SIZE + SCH2_SIZE ) + +#define STAG_SIZE 16 +#define STAG_ID 0x04 +#define STAG_MAGIC 0x2B24 + +#define SCH2_SIZE 40 +#define SCH2_MAGIC 0x2124 +#define SCH2_VER 0x02 + +/* + * Jboot image header, + * all data in little endian. + */ + +struct jimage_header //stag + sch2 jboot joined headers +{ + uint8_t stag_cmark; // in factory 0xFF , in sysupgrade must be the same as stag_id + uint8_t stag_id; // 0x04 + uint16_t stag_magic; //magic 0x2B24 + uint32_t stag_time_stamp; // timestamp calculated in jboot way + uint32_t stag_image_length; // lentgh of kernel + sch2 header + uint16_t stag_image_checksum; // negated jboot_checksum of sch2 + kernel + uint16_t stag_tag_checksum; // negated jboot_checksum of stag header data + uint16_t sch2_magic; // magic 0x2124 + uint8_t sch2_cp_type; // 0x00 for flat, 0x01 for jz, 0x02 for gzip, 0x03 for lzma + uint8_t sch2_version; // 0x02 for sch2 + uint32_t sch2_ram_addr; // ram entry address + uint32_t sch2_image_len; // kernel image length + uint32_t sch2_image_crc32; // kernel image crc + uint32_t sch2_start_addr; // ram start address + uint32_t sch2_rootfs_addr; // rootfs flash address + uint32_t sch2_rootfs_len; // rootfls length + uint32_t sch2_rootfs_crc32; // rootfs crc32 + uint32_t sch2_header_crc32; // sch2 header crc32, durring calculation this area is replaced by zero + uint16_t sch2_header_length; // sch2 header length: 0x28 + uint16_t sch2_cmd_line_length; // cmd line length, known zeros +}; + +static int +read_jimage_header(struct mtd_info *mtd, size_t offset, u_char *buf, + size_t header_len) +{ + size_t retlen; + int ret; + + ret = mtd_read(mtd, offset, header_len, &retlen, buf); + if (ret) { + pr_debug("read error in \"%s\"\n", mtd->name); + return ret; + } + + if (retlen != header_len) { + pr_debug("short read in \"%s\"\n", mtd->name); + return -EIO; + } + + return 0; +} + +/** + * __mtdsplit_parse_jimage - scan partition and create kernel + rootfs parts + * + * @find_header: function to call for a block of data that will return offset + * of a valid jImage header if found + */ +static int __mtdsplit_parse_jimage(struct mtd_info *master, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data, + ssize_t (*find_header)(u_char *buf, size_t len)) +{ + struct mtd_partition *parts; + u_char *buf; + int nr_parts; + size_t offset; + size_t jimage_offset; + size_t jimage_size = 0; + size_t rootfs_offset; + size_t rootfs_size = 0; + int jimage_part, rf_part; + int ret; + enum mtdsplit_part_type type; + + nr_parts = 2; + parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL); + if (!parts) + return -ENOMEM; + + buf = vmalloc(MAX_HEADER_LEN); + if (!buf) { + ret = -ENOMEM; + goto err_free_parts; + } + + /* find jImage on erase block boundaries */ + for (offset = 0; offset < master->size; offset += master->erasesize) { + struct jimage_header *header; + + jimage_size = 0; + + ret = read_jimage_header(master, offset, buf, MAX_HEADER_LEN); + if (ret) + continue; + + ret = find_header(buf, MAX_HEADER_LEN); + if (ret < 0) { + pr_debug("no valid jImage found in \"%s\" at offset %llx\n", + master->name, (unsigned long long) offset); + continue; + } + header = (struct jimage_header *)(buf + ret); + + jimage_size = sizeof(*header) + header->sch2_image_len + ret; + if ((offset + jimage_size) > master->size) { + pr_debug("jImage exceeds MTD device \"%s\"\n", + master->name); + continue; + } + break; + } + + if (jimage_size == 0) { + pr_debug("no jImage found in \"%s\"\n", master->name); + ret = -ENODEV; + goto err_free_buf; + } + + jimage_offset = offset; + + if (jimage_offset == 0) { + jimage_part = 0; + rf_part = 1; + + /* find the roots after the jImage */ + ret = mtd_find_rootfs_from(master, jimage_offset + jimage_size, + master->size, &rootfs_offset, &type); + if (ret) { + pr_debug("no rootfs after jImage in \"%s\"\n", + master->name); + goto err_free_buf; + } + + rootfs_size = master->size - rootfs_offset; + jimage_size = rootfs_offset - jimage_offset; + } else { + rf_part = 0; + jimage_part = 1; + + /* check rootfs presence at offset 0 */ + ret = mtd_check_rootfs_magic(master, 0, &type); + if (ret) { + pr_debug("no rootfs before jImage in \"%s\"\n", + master->name); + goto err_free_buf; + } + + rootfs_offset = 0; + rootfs_size = jimage_offset; + } + + if (rootfs_size == 0) { + pr_debug("no rootfs found in \"%s\"\n", master->name); + ret = -ENODEV; + goto err_free_buf; + } + + parts[jimage_part].name = KERNEL_PART_NAME; + parts[jimage_part].offset = jimage_offset; + parts[jimage_part].size = jimage_size; + + if (type == MTDSPLIT_PART_TYPE_UBI) + parts[rf_part].name = UBI_PART_NAME; + else + parts[rf_part].name = ROOTFS_PART_NAME; + parts[rf_part].offset = rootfs_offset; + parts[rf_part].size = rootfs_size; + + vfree(buf); + + *pparts = parts; + return nr_parts; + +err_free_buf: + vfree(buf); + +err_free_parts: + kfree(parts); + return ret; +} + +static ssize_t jimage_verify_default(u_char *buf, size_t len) +{ + struct jimage_header *header = (struct jimage_header *)buf; + + /* default sanity checks */ + if (header->stag_magic != STAG_MAGIC) { + pr_debug("invalid jImage stag header magic: %04x\n", + header->stag_magic); + return -EINVAL; + } + if (header->sch2_magic != SCH2_MAGIC) { + pr_debug("invalid jImage sch2 header magic: %04x\n", + header->stag_magic); + return -EINVAL; + } + if (header->stag_cmark != header->stag_id) { + pr_debug("invalid jImage stag header cmark: %02x\n", + header->stag_magic); + return -EINVAL; + } + if (header->stag_id != STAG_ID) { + pr_debug("invalid jImage stag header id: %02x\n", + header->stag_magic); + return -EINVAL; + } + if (header->sch2_version != SCH2_VER) { + pr_debug("invalid jImage sch2 header version: %02x\n", + header->stag_magic); + return -EINVAL; + } + + return 0; +} + +static int +mtdsplit_jimage_parse_generic(struct mtd_info *master, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + return __mtdsplit_parse_jimage(master, pparts, data, + jimage_verify_default); +} + +static struct mtd_part_parser jimage_generic_parser = { + .owner = THIS_MODULE, + .name = "jimage-fw", + .parse_fn = mtdsplit_jimage_parse_generic, + .type = MTD_PARSER_TYPE_FIRMWARE, +}; + +/************************************************** + * Init + **************************************************/ + +static int __init mtdsplit_jimage_init(void) +{ + register_mtd_parser(&jimage_generic_parser); + + return 0; +} + +module_init(mtdsplit_jimage_init); |