Initial commit

This commit is contained in:
domenico
2025-06-24 15:51:28 +02:00
commit 22031d9dab
6862 changed files with 1462554 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
config MTD_SPLIT
def_bool n
help
Generic MTD split support.
config MTD_SPLIT_SUPPORT
def_bool MTD = y
comment "Rootfs partition parsers"
config MTD_SPLIT_SQUASHFS_ROOT
bool "Squashfs based root partition parser"
depends on MTD_SPLIT_SUPPORT
select MTD_SPLIT
default n
help
This provides a parsing function which allows to detect the
offset and size of the unused portion of a rootfs partition
containing a squashfs.
comment "Firmware partition parsers"
config MTD_SPLIT_SEAMA_FW
bool "Seama firmware parser"
depends on MTD_SPLIT_SUPPORT
select MTD_SPLIT
config MTD_SPLIT_WRGG_FW
bool "WRGG firmware parser"
depends on MTD_SPLIT_SUPPORT
select MTD_SPLIT
config MTD_SPLIT_UIMAGE_FW
bool "uImage based firmware partition parser"
depends on MTD_SPLIT_SUPPORT
select MTD_SPLIT
config MTD_SPLIT_FIT_FW
bool "FIT based firmware partition parser"
depends on MTD_SPLIT_SUPPORT
select MTD_SPLIT
config MTD_SPLIT_LZMA_FW
bool "LZMA compressed kernel based firmware partition parser"
depends on MTD_SPLIT_SUPPORT
select MTD_SPLIT
config MTD_SPLIT_TPLINK_FW
bool "TP-Link firmware parser"
depends on MTD_SPLIT_SUPPORT
select MTD_SPLIT
config MTD_SPLIT_TRX_FW
bool "TRX image based firmware partition parser"
depends on MTD_SPLIT_SUPPORT
select MTD_SPLIT
config MTD_SPLIT_BRNIMAGE_FW
bool "brnImage (brnboot image) firmware parser"
depends on MTD_SPLIT_SUPPORT
select MTD_SPLIT
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
config MTD_SPLIT_JIMAGE_FW
bool "JBOOT Image based firmware partition parser"
depends on MTD_SPLIT_SUPPORT
select MTD_SPLIT

View File

@@ -0,0 +1,13 @@
obj-$(CONFIG_MTD_SPLIT) += mtdsplit.o
obj-$(CONFIG_MTD_SPLIT_SEAMA_FW) += mtdsplit_seama.o
obj-$(CONFIG_MTD_SPLIT_SQUASHFS_ROOT) += mtdsplit_squashfs.o
obj-$(CONFIG_MTD_SPLIT_UIMAGE_FW) += mtdsplit_uimage.o
obj-$(CONFIG_MTD_SPLIT_FIT_FW) += mtdsplit_fit.o
obj-$(CONFIG_MTD_SPLIT_LZMA_FW) += mtdsplit_lzma.o
obj-$(CONFIG_MTD_SPLIT_TPLINK_FW) += mtdsplit_tplink.o
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
obj-$(CONFIG_MTD_SPLIT_JIMAGE_FW) += mtdsplit_jimage.o

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2009-2013 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2009-2013 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2012 Jonas Gorski <jogo@openwrt.org>
* Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
*
* 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) "mtdsplit: " fmt
#include <linux/export.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/magic.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/byteorder/generic.h>
#include "mtdsplit.h"
#define UBI_EC_MAGIC 0x55424923 /* UBI# */
struct squashfs_super_block {
__le32 s_magic;
__le32 pad0[9];
__le64 bytes_used;
};
int mtd_get_squashfs_len(struct mtd_info *master,
size_t offset,
size_t *squashfs_len)
{
struct squashfs_super_block sb;
size_t retlen;
int err;
err = mtd_read(master, offset, sizeof(sb), &retlen, (void *)&sb);
if (err || (retlen != sizeof(sb))) {
pr_alert("error occured while reading from \"%s\"\n",
master->name);
return -EIO;
}
if (le32_to_cpu(sb.s_magic) != SQUASHFS_MAGIC) {
pr_alert("no squashfs found in \"%s\"\n", master->name);
return -EINVAL;
}
retlen = le64_to_cpu(sb.bytes_used);
if (retlen <= 0) {
pr_alert("squashfs is empty in \"%s\"\n", master->name);
return -ENODEV;
}
if (offset + retlen > master->size) {
pr_alert("squashfs has invalid size in \"%s\"\n",
master->name);
return -EINVAL;
}
*squashfs_len = retlen;
return 0;
}
EXPORT_SYMBOL_GPL(mtd_get_squashfs_len);
static ssize_t mtd_next_eb(struct mtd_info *mtd, size_t offset)
{
return mtd_rounddown_to_eb(offset, mtd) + mtd->erasesize;
}
int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
enum mtdsplit_part_type *type)
{
u32 magic;
size_t retlen;
int ret;
ret = mtd_read(mtd, offset, sizeof(magic), &retlen,
(unsigned char *) &magic);
if (ret)
return ret;
if (retlen != sizeof(magic))
return -EIO;
if (le32_to_cpu(magic) == SQUASHFS_MAGIC) {
if (type)
*type = MTDSPLIT_PART_TYPE_SQUASHFS;
return 0;
} else if (magic == 0x19852003) {
if (type)
*type = MTDSPLIT_PART_TYPE_JFFS2;
return 0;
} else if (be32_to_cpu(magic) == UBI_EC_MAGIC) {
if (type)
*type = MTDSPLIT_PART_TYPE_UBI;
return 0;
}
return -EINVAL;
}
EXPORT_SYMBOL_GPL(mtd_check_rootfs_magic);
int mtd_find_rootfs_from(struct mtd_info *mtd,
size_t from,
size_t limit,
size_t *ret_offset,
enum mtdsplit_part_type *type)
{
size_t offset;
int err;
for (offset = from; offset < limit;
offset = mtd_next_eb(mtd, offset)) {
err = mtd_check_rootfs_magic(mtd, offset, type);
if (err)
continue;
*ret_offset = offset;
return 0;
}
return -ENODEV;
}
EXPORT_SYMBOL_GPL(mtd_find_rootfs_from);

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2009-2013 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2009-2013 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2012 Jonas Gorski <jogo@openwrt.org>
* Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
*
* 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.
*
*/
#ifndef _MTDSPLIT_H
#define _MTDSPLIT_H
#define KERNEL_PART_NAME "kernel"
#define ROOTFS_PART_NAME "rootfs"
#define UBI_PART_NAME "ubi"
#define ROOTFS_SPLIT_NAME "rootfs_data"
enum mtdsplit_part_type {
MTDSPLIT_PART_TYPE_UNK = 0,
MTDSPLIT_PART_TYPE_SQUASHFS,
MTDSPLIT_PART_TYPE_JFFS2,
MTDSPLIT_PART_TYPE_UBI,
};
#ifdef CONFIG_MTD_SPLIT
int mtd_get_squashfs_len(struct mtd_info *master,
size_t offset,
size_t *squashfs_len);
int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
enum mtdsplit_part_type *type);
int mtd_find_rootfs_from(struct mtd_info *mtd,
size_t from,
size_t limit,
size_t *ret_offset,
enum mtdsplit_part_type *type);
#else
static inline int mtd_get_squashfs_len(struct mtd_info *master,
size_t offset,
size_t *squashfs_len)
{
return -ENODEV;
}
static inline int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
enum mtdsplit_part_type *type)
{
return -EINVAL;
}
static inline int mtd_find_rootfs_from(struct mtd_info *mtd,
size_t from,
size_t limit,
size_t *ret_offset,
enum mtdsplit_part_type *type)
{
return -ENODEV;
}
#endif /* CONFIG_MTD_SPLIT */
#endif /* _MTDSPLIT_H */

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2012 John Crispin <blogic@openwrt.org>
* Copyright (C) 2015 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/byteorder/generic.h>
#include "mtdsplit.h"
#define BRNIMAGE_NR_PARTS 2
#define BRNIMAGE_ALIGN_BYTES 0x400
#define BRNIMAGE_FOOTER_SIZE 12
#define BRNIMAGE_MIN_OVERHEAD (BRNIMAGE_FOOTER_SIZE)
#define BRNIMAGE_MAX_OVERHEAD (BRNIMAGE_ALIGN_BYTES + BRNIMAGE_FOOTER_SIZE)
static int mtdsplit_parse_brnimage(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct mtd_partition *parts;
uint32_t buf;
unsigned long rootfs_offset, rootfs_size, kernel_size;
size_t len;
int ret = 0;
for (rootfs_offset = 0; rootfs_offset < master->size;
rootfs_offset += BRNIMAGE_ALIGN_BYTES) {
ret = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
if (!ret)
break;
}
if (ret)
return ret;
if (rootfs_offset >= master->size)
return -EINVAL;
ret = mtd_read(master, rootfs_offset - BRNIMAGE_FOOTER_SIZE, 4, &len,
(void *)&buf);
if (ret)
return ret;
if (len != 4)
return -EIO;
kernel_size = le32_to_cpu(buf);
if (kernel_size > (rootfs_offset - BRNIMAGE_MIN_OVERHEAD))
return -EINVAL;
if (kernel_size < (rootfs_offset - BRNIMAGE_MAX_OVERHEAD))
return -EINVAL;
/*
* The footer must be untouched as it contains the checksum of the
* original brnImage (kernel + squashfs)!
*/
rootfs_size = master->size - rootfs_offset - BRNIMAGE_FOOTER_SIZE;
parts = kzalloc(BRNIMAGE_NR_PARTS * sizeof(*parts), GFP_KERNEL);
if (!parts)
return -ENOMEM;
parts[0].name = KERNEL_PART_NAME;
parts[0].offset = 0;
parts[0].size = kernel_size;
parts[1].name = ROOTFS_PART_NAME;
parts[1].offset = rootfs_offset;
parts[1].size = rootfs_size;
*pparts = parts;
return BRNIMAGE_NR_PARTS;
}
static struct mtd_part_parser mtdsplit_brnimage_parser = {
.owner = THIS_MODULE,
.name = "brnimage-fw",
.parse_fn = mtdsplit_parse_brnimage,
.type = MTD_PARSER_TYPE_FIRMWARE,
};
static int __init mtdsplit_brnimage_init(void)
{
register_mtd_parser(&mtdsplit_brnimage_parser);
return 0;
}
subsys_initcall(mtdsplit_brnimage_init);

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2012 John Crispin <blogic@openwrt.org>
* Copyright (C) 2015 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/byteorder/generic.h>
#include "mtdsplit.h"
#define EVA_NR_PARTS 2
#define EVA_MAGIC 0xfeed1281
#define EVA_FOOTER_SIZE 0x18
#define EVA_DUMMY_SQUASHFS_SIZE 0x100
struct eva_image_header {
uint32_t magic;
uint32_t size;
};
static int mtdsplit_parse_eva(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct mtd_partition *parts;
struct eva_image_header hdr;
size_t retlen;
unsigned long kernel_size, rootfs_offset;
int err;
err = mtd_read(master, 0, sizeof(hdr), &retlen, (void *) &hdr);
if (err)
return err;
if (retlen != sizeof(hdr))
return -EIO;
if (le32_to_cpu(hdr.magic) != EVA_MAGIC)
return -EINVAL;
kernel_size = le32_to_cpu(hdr.size) + EVA_FOOTER_SIZE;
/* rootfs starts at the next 0x10000 boundary: */
rootfs_offset = round_up(kernel_size, 0x10000);
/* skip the dummy EVA squashfs partition (with wrong endianness): */
rootfs_offset += EVA_DUMMY_SQUASHFS_SIZE;
if (rootfs_offset >= master->size)
return -EINVAL;
err = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
if (err)
return err;
parts = kzalloc(EVA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
if (!parts)
return -ENOMEM;
parts[0].name = KERNEL_PART_NAME;
parts[0].offset = 0;
parts[0].size = kernel_size;
parts[1].name = ROOTFS_PART_NAME;
parts[1].offset = rootfs_offset;
parts[1].size = master->size - rootfs_offset;
*pparts = parts;
return EVA_NR_PARTS;
}
static struct mtd_part_parser mtdsplit_eva_parser = {
.owner = THIS_MODULE,
.name = "eva-fw",
.parse_fn = mtdsplit_parse_eva,
.type = MTD_PARSER_TYPE_FIRMWARE,
};
static int __init mtdsplit_eva_init(void)
{
register_mtd_parser(&mtdsplit_eva_parser);
return 0;
}
subsys_initcall(mtdsplit_eva_init);

View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) 2015 The Linux Foundation
* Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/types.h>
#include <linux/byteorder/generic.h>
#include <linux/slab.h>
#include <linux/of_fdt.h>
#include "mtdsplit.h"
struct fdt_header {
uint32_t magic; /* magic word FDT_MAGIC */
uint32_t totalsize; /* total size of DT block */
uint32_t off_dt_struct; /* offset to structure */
uint32_t off_dt_strings; /* offset to strings */
uint32_t off_mem_rsvmap; /* offset to memory reserve map */
uint32_t version; /* format version */
uint32_t last_comp_version; /* last compatible version */
/* version 2 fields below */
uint32_t boot_cpuid_phys; /* Which physical CPU id we're
booting on */
/* version 3 fields below */
uint32_t size_dt_strings; /* size of the strings block */
/* version 17 fields below */
uint32_t size_dt_struct; /* size of the structure block */
};
static int
mtdsplit_fit_parse(struct mtd_info *mtd,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct fdt_header hdr;
size_t hdr_len, retlen;
size_t offset;
size_t fit_offset, fit_size;
size_t rootfs_offset, rootfs_size;
struct mtd_partition *parts;
int ret;
hdr_len = sizeof(struct fdt_header);
/* Parse the MTD device & search for the FIT image location */
for(offset = 0; offset < mtd->size; offset += mtd->erasesize) {
ret = mtd_read(mtd, 0, hdr_len, &retlen, (void*) &hdr);
if (ret) {
pr_err("read error in \"%s\" at offset 0x%llx\n",
mtd->name, (unsigned long long) offset);
return ret;
}
if (retlen != hdr_len) {
pr_err("short read in \"%s\"\n", mtd->name);
return -EIO;
}
/* Check the magic - see if this is a FIT image */
if (be32_to_cpu(hdr.magic) != OF_DT_HEADER) {
pr_debug("no valid FIT image found in \"%s\" at offset %llx\n",
mtd->name, (unsigned long long) offset);
continue;
}
/* We found a FIT image. Let's keep going */
break;
}
fit_offset = offset;
fit_size = be32_to_cpu(hdr.totalsize);
if (fit_size == 0) {
pr_err("FIT image in \"%s\" at offset %llx has null size\n",
mtd->name, (unsigned long long) fit_offset);
return -ENODEV;
}
/* Search for the rootfs partition after the FIT image */
ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size, mtd->size,
&rootfs_offset, NULL);
if (ret) {
pr_info("no rootfs found after FIT image in \"%s\"\n",
mtd->name);
return ret;
}
rootfs_size = mtd->size - rootfs_offset;
parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL);
if (!parts)
return -ENOMEM;
parts[0].name = KERNEL_PART_NAME;
parts[0].offset = fit_offset;
parts[0].size = mtd_rounddown_to_eb(fit_size, mtd) + mtd->erasesize;
parts[1].name = ROOTFS_PART_NAME;
parts[1].offset = rootfs_offset;
parts[1].size = rootfs_size;
*pparts = parts;
return 2;
}
static struct mtd_part_parser uimage_parser = {
.owner = THIS_MODULE,
.name = "fit-fw",
.parse_fn = mtdsplit_fit_parse,
.type = MTD_PARSER_TYPE_FIRMWARE,
};
/**************************************************
* Init
**************************************************/
static int __init mtdsplit_fit_init(void)
{
register_mtd_parser(&uimage_parser);
return 0;
}
module_init(mtdsplit_fit_init);

View File

@@ -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);

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2014 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.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <asm/unaligned.h>
#include "mtdsplit.h"
#define LZMA_NR_PARTS 2
#define LZMA_PROPERTIES_SIZE 5
struct lzma_header {
u8 props[LZMA_PROPERTIES_SIZE];
u8 size_low[4];
u8 size_high[4];
};
static int mtdsplit_parse_lzma(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct lzma_header hdr;
size_t hdr_len, retlen;
size_t rootfs_offset;
u32 t;
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;
/* verify LZMA properties */
if (hdr.props[0] >= (9 * 5 * 5))
return -EINVAL;
t = get_unaligned_le32(&hdr.props[1]);
if (!is_power_of_2(t))
return -EINVAL;
t = get_unaligned_le32(&hdr.size_high);
if (t)
return -EINVAL;
err = mtd_find_rootfs_from(master, master->erasesize, master->size,
&rootfs_offset, NULL);
if (err)
return err;
parts = kzalloc(LZMA_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 LZMA_NR_PARTS;
}
static struct mtd_part_parser mtdsplit_lzma_parser = {
.owner = THIS_MODULE,
.name = "lzma-fw",
.parse_fn = mtdsplit_parse_lzma,
.type = MTD_PARSER_TYPE_FIRMWARE,
};
static int __init mtdsplit_lzma_init(void)
{
register_mtd_parser(&mtdsplit_lzma_parser);
return 0;
}
subsys_initcall(mtdsplit_lzma_init);

View File

@@ -0,0 +1,117 @@
/*
* MTD splitter for MikroTik NOR devices
*
* Copyright (C) 2017 Thibaut VARENE <varenet@parisc-linux.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.
*
* 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 <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/string.h>
#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);

View File

@@ -0,0 +1,110 @@
/*
* 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.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/byteorder/generic.h>
#include "mtdsplit.h"
#define SEAMA_MAGIC 0x5EA3A417
#define SEAMA_NR_PARTS 2
#define SEAMA_MIN_ROOTFS_OFFS 0x80000 /* 512KiB */
struct seama_header {
__be32 magic; /* should always be SEAMA_MAGIC. */
__be16 reserved; /* reserved for */
__be16 metasize; /* size of the META data */
__be32 size; /* size of the image */
u8 md5[16]; /* digest */
};
static int mtdsplit_parse_seama(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct seama_header hdr;
size_t hdr_len, retlen, kernel_ent_size;
size_t rootfs_offset;
struct mtd_partition *parts;
enum mtdsplit_part_type type;
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;
/* sanity checks */
if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC)
return -EINVAL;
kernel_ent_size = hdr_len + be32_to_cpu(hdr.size) +
be16_to_cpu(hdr.metasize);
if (kernel_ent_size > master->size)
return -EINVAL;
/* Check for the rootfs right after Seama entity with a kernel. */
err = mtd_check_rootfs_magic(master, kernel_ent_size, &type);
if (!err) {
rootfs_offset = kernel_ent_size;
} else {
/*
* On some devices firmware entity might contain both: kernel
* and rootfs. We can't determine kernel size so we just have to
* look for rootfs magic.
* Start the search from an arbitrary offset.
*/
err = mtd_find_rootfs_from(master, SEAMA_MIN_ROOTFS_OFFS,
master->size, &rootfs_offset, &type);
if (err)
return err;
}
parts = kzalloc(SEAMA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
if (!parts)
return -ENOMEM;
parts[0].name = KERNEL_PART_NAME;
parts[0].offset = sizeof hdr + be16_to_cpu(hdr.metasize);
parts[0].size = rootfs_offset - parts[0].offset;
if (type == MTDSPLIT_PART_TYPE_UBI)
parts[1].name = UBI_PART_NAME;
else
parts[1].name = ROOTFS_PART_NAME;
parts[1].offset = rootfs_offset;
parts[1].size = master->size - rootfs_offset;
*pparts = parts;
return SEAMA_NR_PARTS;
}
static struct mtd_part_parser mtdsplit_seama_parser = {
.owner = THIS_MODULE,
.name = "seama-fw",
.parse_fn = mtdsplit_parse_seama,
.type = MTD_PARSER_TYPE_FIRMWARE,
};
static int __init mtdsplit_seama_init(void)
{
register_mtd_parser(&mtdsplit_seama_parser);
return 0;
}
subsys_initcall(mtdsplit_seama_init);

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2013 Felix Fietkau <nbd@nbd.name>
* 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/magic.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/byteorder/generic.h>
#include "mtdsplit.h"
static int
mtdsplit_parse_squashfs(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct mtd_partition *part;
struct mtd_info *parent_mtd;
size_t part_offset;
size_t squashfs_len;
int err;
err = mtd_get_squashfs_len(master, 0, &squashfs_len);
if (err)
return err;
parent_mtd = mtdpart_get_master(master);
part_offset = mtdpart_get_offset(master);
part = kzalloc(sizeof(*part), GFP_KERNEL);
if (!part) {
pr_alert("unable to allocate memory for \"%s\" partition\n",
ROOTFS_SPLIT_NAME);
return -ENOMEM;
}
part->name = ROOTFS_SPLIT_NAME;
part->offset = mtd_roundup_to_eb(part_offset + squashfs_len,
parent_mtd) - part_offset;
part->size = mtd_rounddown_to_eb(master->size - part->offset, master);
*pparts = part;
return 1;
}
static struct mtd_part_parser mtdsplit_squashfs_parser = {
.owner = THIS_MODULE,
.name = "squashfs-split",
.parse_fn = mtdsplit_parse_squashfs,
.type = MTD_PARSER_TYPE_ROOTFS,
};
static int __init mtdsplit_squashfs_init(void)
{
register_mtd_parser(&mtdsplit_squashfs_parser);
return 0;
}
subsys_initcall(mtdsplit_squashfs_init);

View File

@@ -0,0 +1,169 @@
/*
* Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/byteorder/generic.h>
#include "mtdsplit.h"
#define TPLINK_NR_PARTS 2
#define TPLINK_MIN_ROOTFS_OFFS 0x80000 /* 512KiB */
#define MD5SUM_LEN 16
struct fw_v1 {
char vendor_name[24];
char fw_version[36];
uint32_t hw_id; /* hardware id */
uint32_t hw_rev; /* hardware revision */
uint32_t unk1;
uint8_t md5sum1[MD5SUM_LEN];
uint32_t unk2;
uint8_t md5sum2[MD5SUM_LEN];
uint32_t unk3;
uint32_t kernel_la; /* kernel load address */
uint32_t kernel_ep; /* kernel entry point */
uint32_t fw_length; /* total length of the firmware */
uint32_t kernel_ofs; /* kernel data offset */
uint32_t kernel_len; /* kernel data length */
uint32_t rootfs_ofs; /* rootfs data offset */
uint32_t rootfs_len; /* rootfs data length */
uint32_t boot_ofs; /* bootloader data offset */
uint32_t boot_len; /* bootloader data length */
uint8_t pad[360];
} __attribute__ ((packed));
struct fw_v2 {
char fw_version[48]; /* 0x04: fw version string */
uint32_t hw_id; /* 0x34: hardware id */
uint32_t hw_rev; /* 0x38: FIXME: hardware revision? */
uint32_t unk1; /* 0x3c: 0x00000000 */
uint8_t md5sum1[MD5SUM_LEN]; /* 0x40 */
uint32_t unk2; /* 0x50: 0x00000000 */
uint8_t md5sum2[MD5SUM_LEN]; /* 0x54 */
uint32_t unk3; /* 0x64: 0xffffffff */
uint32_t kernel_la; /* 0x68: kernel load address */
uint32_t kernel_ep; /* 0x6c: kernel entry point */
uint32_t fw_length; /* 0x70: total length of the image */
uint32_t kernel_ofs; /* 0x74: kernel data offset */
uint32_t kernel_len; /* 0x78: kernel data length */
uint32_t rootfs_ofs; /* 0x7c: rootfs data offset */
uint32_t rootfs_len; /* 0x80: rootfs data length */
uint32_t boot_ofs; /* 0x84: FIXME: seems to be unused */
uint32_t boot_len; /* 0x88: FIXME: seems to be unused */
uint16_t unk4; /* 0x8c: 0x55aa */
uint8_t sver_hi; /* 0x8e */
uint8_t sver_lo; /* 0x8f */
uint8_t unk5; /* 0x90: magic: 0xa5 */
uint8_t ver_hi; /* 0x91 */
uint8_t ver_mid; /* 0x92 */
uint8_t ver_lo; /* 0x93 */
uint8_t pad[364];
} __attribute__ ((packed));
struct tplink_fw_header {
uint32_t version;
union {
struct fw_v1 v1;
struct fw_v2 v2;
};
};
static int mtdsplit_parse_tplink(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct tplink_fw_header hdr;
size_t hdr_len, retlen, kernel_size;
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;
switch (le32_to_cpu(hdr.version)) {
case 1:
if (be32_to_cpu(hdr.v1.kernel_ofs) != sizeof(hdr))
return -EINVAL;
kernel_size = sizeof(hdr) + be32_to_cpu(hdr.v1.kernel_len);
rootfs_offset = be32_to_cpu(hdr.v1.rootfs_ofs);
break;
case 2:
case 3:
if (be32_to_cpu(hdr.v2.kernel_ofs) != sizeof(hdr))
return -EINVAL;
kernel_size = sizeof(hdr) + be32_to_cpu(hdr.v2.kernel_len);
rootfs_offset = be32_to_cpu(hdr.v2.rootfs_ofs);
break;
default:
return -EINVAL;
}
if (kernel_size > master->size)
return -EINVAL;
/* Find the rootfs */
err = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
if (err) {
/*
* The size in the header might cover the rootfs as well.
* Start the search from an arbitrary offset.
*/
err = mtd_find_rootfs_from(master, TPLINK_MIN_ROOTFS_OFFS,
master->size, &rootfs_offset, NULL);
if (err)
return err;
}
parts = kzalloc(TPLINK_NR_PARTS * sizeof(*parts), GFP_KERNEL);
if (!parts)
return -ENOMEM;
parts[0].name = KERNEL_PART_NAME;
parts[0].offset = 0;
parts[0].size = kernel_size;
parts[1].name = ROOTFS_PART_NAME;
parts[1].offset = rootfs_offset;
parts[1].size = master->size - rootfs_offset;
*pparts = parts;
return TPLINK_NR_PARTS;
}
static struct mtd_part_parser mtdsplit_tplink_parser = {
.owner = THIS_MODULE,
.name = "tplink-fw",
.parse_fn = mtdsplit_parse_tplink,
.type = MTD_PARSER_TYPE_FIRMWARE,
};
static int __init mtdsplit_tplink_init(void)
{
register_mtd_parser(&mtdsplit_tplink_parser);
return 0;
}
subsys_initcall(mtdsplit_tplink_init);

View File

@@ -0,0 +1,147 @@
/*
* Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
*
* 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/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/byteorder/generic.h>
#include "mtdsplit.h"
#define TRX_MAGIC 0x30524448 /* "HDR0" */
struct trx_header {
__le32 magic;
__le32 len;
__le32 crc32;
__le32 flag_version;
__le32 offset[4];
};
static int
read_trx_header(struct mtd_info *mtd, size_t offset,
struct trx_header *header)
{
size_t header_len;
size_t retlen;
int ret;
header_len = sizeof(*header);
ret = mtd_read(mtd, offset, header_len, &retlen,
(unsigned char *) header);
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;
}
static int
mtdsplit_parse_trx(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct mtd_partition *parts;
struct trx_header hdr;
int nr_parts;
size_t offset;
size_t trx_offset;
size_t trx_size = 0;
size_t rootfs_offset;
size_t rootfs_size = 0;
int ret;
nr_parts = 2;
parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
if (!parts)
return -ENOMEM;
/* find trx image on erase block boundaries */
for (offset = 0; offset < master->size; offset += master->erasesize) {
trx_size = 0;
ret = read_trx_header(master, offset, &hdr);
if (ret)
continue;
if (hdr.magic != cpu_to_le32(TRX_MAGIC)) {
pr_debug("no valid trx header found in \"%s\" at offset %llx\n",
master->name, (unsigned long long) offset);
continue;
}
trx_size = le32_to_cpu(hdr.len);
if ((offset + trx_size) > master->size) {
pr_debug("trx image exceeds MTD device \"%s\"\n",
master->name);
continue;
}
break;
}
if (trx_size == 0) {
pr_debug("no trx header found in \"%s\"\n", master->name);
ret = -ENODEV;
goto err;
}
trx_offset = offset + hdr.offset[0];
rootfs_offset = offset + hdr.offset[1];
rootfs_size = master->size - rootfs_offset;
trx_size = rootfs_offset - trx_offset;
if (rootfs_size == 0) {
pr_debug("no rootfs found in \"%s\"\n", master->name);
ret = -ENODEV;
goto err;
}
parts[0].name = KERNEL_PART_NAME;
parts[0].offset = trx_offset;
parts[0].size = trx_size;
parts[1].name = ROOTFS_PART_NAME;
parts[1].offset = rootfs_offset;
parts[1].size = rootfs_size;
*pparts = parts;
return nr_parts;
err:
kfree(parts);
return ret;
}
static struct mtd_part_parser trx_parser = {
.owner = THIS_MODULE,
.name = "trx-fw",
.parse_fn = mtdsplit_parse_trx,
.type = MTD_PARSER_TYPE_FIRMWARE,
};
static int __init mtdsplit_trx_init(void)
{
register_mtd_parser(&trx_parser);
return 0;
}
module_init(mtdsplit_trx_init);

View File

@@ -0,0 +1,361 @@
/*
* 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"
/*
* uimage_header itself is only 64B, but it may be prepended with another data.
* Currently the biggest size is for Edimax devices: 20B + 64B
*/
#define MAX_HEADER_LEN 84
#define IH_MAGIC 0x27051956 /* Image Magic Number */
#define IH_NMLEN 32 /* Image Name Length */
#define IH_OS_LINUX 5 /* Linux */
#define IH_TYPE_KERNEL 2 /* OS Kernel Image */
#define IH_TYPE_FILESYSTEM 7 /* Filesystem Image */
/*
* Legacy format image header,
* all data in network byte order (aka natural aka bigendian).
*/
struct uimage_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address */
uint32_t ih_ep; /* Entry Point Address */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
};
static int
read_uimage_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_uimage - scan partition and create kernel + rootfs parts
*
* @find_header: function to call for a block of data that will return offset
* of a valid uImage header if found
*/
static int __mtdsplit_parse_uimage(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 uimage_offset;
size_t uimage_size = 0;
size_t rootfs_offset;
size_t rootfs_size = 0;
int uimage_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 uImage on erase block boundaries */
for (offset = 0; offset < master->size; offset += master->erasesize) {
struct uimage_header *header;
uimage_size = 0;
ret = read_uimage_header(master, offset, buf, MAX_HEADER_LEN);
if (ret)
continue;
ret = find_header(buf, MAX_HEADER_LEN);
if (ret < 0) {
pr_debug("no valid uImage found in \"%s\" at offset %llx\n",
master->name, (unsigned long long) offset);
continue;
}
header = (struct uimage_header *)(buf + ret);
uimage_size = sizeof(*header) + be32_to_cpu(header->ih_size) + ret;
if ((offset + uimage_size) > master->size) {
pr_debug("uImage exceeds MTD device \"%s\"\n",
master->name);
continue;
}
break;
}
if (uimage_size == 0) {
pr_debug("no uImage found in \"%s\"\n", master->name);
ret = -ENODEV;
goto err_free_buf;
}
uimage_offset = offset;
if (uimage_offset == 0) {
uimage_part = 0;
rf_part = 1;
/* find the roots after the uImage */
ret = mtd_find_rootfs_from(master, uimage_offset + uimage_size,
master->size, &rootfs_offset, &type);
if (ret) {
pr_debug("no rootfs after uImage in \"%s\"\n",
master->name);
goto err_free_buf;
}
rootfs_size = master->size - rootfs_offset;
uimage_size = rootfs_offset - uimage_offset;
} else {
rf_part = 0;
uimage_part = 1;
/* check rootfs presence at offset 0 */
ret = mtd_check_rootfs_magic(master, 0, &type);
if (ret) {
pr_debug("no rootfs before uImage in \"%s\"\n",
master->name);
goto err_free_buf;
}
rootfs_offset = 0;
rootfs_size = uimage_offset;
}
if (rootfs_size == 0) {
pr_debug("no rootfs found in \"%s\"\n", master->name);
ret = -ENODEV;
goto err_free_buf;
}
parts[uimage_part].name = KERNEL_PART_NAME;
parts[uimage_part].offset = uimage_offset;
parts[uimage_part].size = uimage_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 uimage_verify_default(u_char *buf, size_t len)
{
struct uimage_header *header = (struct uimage_header *)buf;
/* default sanity checks */
if (be32_to_cpu(header->ih_magic) != IH_MAGIC) {
pr_debug("invalid uImage magic: %08x\n",
be32_to_cpu(header->ih_magic));
return -EINVAL;
}
if (header->ih_os != IH_OS_LINUX) {
pr_debug("invalid uImage OS: %08x\n",
be32_to_cpu(header->ih_os));
return -EINVAL;
}
if (header->ih_type != IH_TYPE_KERNEL) {
pr_debug("invalid uImage type: %08x\n",
be32_to_cpu(header->ih_type));
return -EINVAL;
}
return 0;
}
static int
mtdsplit_uimage_parse_generic(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
return __mtdsplit_parse_uimage(master, pparts, data,
uimage_verify_default);
}
static struct mtd_part_parser uimage_generic_parser = {
.owner = THIS_MODULE,
.name = "uimage-fw",
.parse_fn = mtdsplit_uimage_parse_generic,
.type = MTD_PARSER_TYPE_FIRMWARE,
};
#define FW_MAGIC_WNR2000V1 0x32303031
#define FW_MAGIC_WNR2000V3 0x32303033
#define FW_MAGIC_WNR2000V4 0x32303034
#define FW_MAGIC_WNR2200 0x32323030
#define FW_MAGIC_WNR612V2 0x32303631
#define FW_MAGIC_WNR1000V2 0x31303031
#define FW_MAGIC_WNR1000V2_VC 0x31303030
#define FW_MAGIC_WNDR3700 0x33373030
#define FW_MAGIC_WNDR3700V2 0x33373031
#define FW_MAGIC_WPN824N 0x31313030
static ssize_t uimage_verify_wndr3700(u_char *buf, size_t len)
{
struct uimage_header *header = (struct uimage_header *)buf;
uint8_t expected_type = IH_TYPE_FILESYSTEM;
switch (be32_to_cpu(header->ih_magic)) {
case FW_MAGIC_WNR612V2:
case FW_MAGIC_WNR1000V2:
case FW_MAGIC_WNR1000V2_VC:
case FW_MAGIC_WNR2000V1:
case FW_MAGIC_WNR2000V3:
case FW_MAGIC_WNR2200:
case FW_MAGIC_WNDR3700:
case FW_MAGIC_WNDR3700V2:
case FW_MAGIC_WPN824N:
break;
case FW_MAGIC_WNR2000V4:
expected_type = IH_TYPE_KERNEL;
break;
default:
return -EINVAL;
}
if (header->ih_os != IH_OS_LINUX ||
header->ih_type != expected_type)
return -EINVAL;
return 0;
}
static int
mtdsplit_uimage_parse_netgear(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
return __mtdsplit_parse_uimage(master, pparts, data,
uimage_verify_wndr3700);
}
static struct mtd_part_parser uimage_netgear_parser = {
.owner = THIS_MODULE,
.name = "netgear-fw",
.parse_fn = mtdsplit_uimage_parse_netgear,
.type = MTD_PARSER_TYPE_FIRMWARE,
};
/**************************************************
* Edimax
**************************************************/
#define FW_EDIMAX_OFFSET 20
#define FW_MAGIC_EDIMAX 0x43535953
static ssize_t uimage_find_edimax(u_char *buf, size_t len)
{
u32 *magic;
if (len < FW_EDIMAX_OFFSET + sizeof(struct uimage_header)) {
pr_err("Buffer too small for checking Edimax header\n");
return -ENOSPC;
}
magic = (u32 *)buf;
if (be32_to_cpu(*magic) != FW_MAGIC_EDIMAX)
return -EINVAL;
if (!uimage_verify_default(buf + FW_EDIMAX_OFFSET, len))
return FW_EDIMAX_OFFSET;
return -EINVAL;
}
static int
mtdsplit_uimage_parse_edimax(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
return __mtdsplit_parse_uimage(master, pparts, data,
uimage_find_edimax);
}
static struct mtd_part_parser uimage_edimax_parser = {
.owner = THIS_MODULE,
.name = "edimax-fw",
.parse_fn = mtdsplit_uimage_parse_edimax,
.type = MTD_PARSER_TYPE_FIRMWARE,
};
/**************************************************
* Init
**************************************************/
static int __init mtdsplit_uimage_init(void)
{
register_mtd_parser(&uimage_generic_parser);
register_mtd_parser(&uimage_netgear_parser);
register_mtd_parser(&uimage_edimax_parser);
return 0;
}
module_init(mtdsplit_uimage_init);

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2016 Stijn Tintel <stijn@linux-ipv6.be>
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/byteorder/generic.h>
#include "mtdsplit.h"
#define WRGG_NR_PARTS 2
#define WRGG_MIN_ROOTFS_OFFS 0x80000 /* 512KiB */
#define WRGG03_MAGIC 0x20080321
#define WRG_MAGIC 0x20040220
struct wrgg03_header {
char signature[32];
uint32_t magic1;
uint32_t magic2;
char version[16];
char model[16];
uint32_t flag[2];
uint32_t reserve[2];
char buildno[16];
uint32_t size;
uint32_t offset;
char devname[32];
char digest[16];
} __attribute__ ((packed));
struct wrg_header {
char signature[32];
uint32_t magic1;
uint32_t magic2;
uint32_t size;
uint32_t offset;
char devname[32];
char digest[16];
} __attribute__ ((packed));
static int mtdsplit_parse_wrgg(struct mtd_info *master,
const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct wrgg03_header hdr;
size_t hdr_len, retlen, kernel_ent_size;
size_t rootfs_offset;
struct mtd_partition *parts;
enum mtdsplit_part_type type;
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;
/* sanity checks */
if (le32_to_cpu(hdr.magic1) == WRGG03_MAGIC) {
kernel_ent_size = hdr_len + be32_to_cpu(hdr.size);
} else if (le32_to_cpu(hdr.magic1) == WRG_MAGIC) {
kernel_ent_size = sizeof(struct wrg_header) + le32_to_cpu(
((struct wrg_header*)&hdr)->size);
} else {
return -EINVAL;
}
if (kernel_ent_size > master->size)
return -EINVAL;
/*
* The size in the header covers the rootfs as well.
* Start the search from an arbitrary offset.
*/
err = mtd_find_rootfs_from(master, WRGG_MIN_ROOTFS_OFFS,
master->size, &rootfs_offset, &type);
if (err)
return err;
parts = kzalloc(WRGG_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 WRGG_NR_PARTS;
}
static struct mtd_part_parser mtdsplit_wrgg_parser = {
.owner = THIS_MODULE,
.name = "wrgg-fw",
.parse_fn = mtdsplit_parse_wrgg,
.type = MTD_PARSER_TYPE_FIRMWARE,
};
static int __init mtdsplit_wrgg_init(void)
{
register_mtd_parser(&mtdsplit_wrgg_parser);
return 0;
}
subsys_initcall(mtdsplit_wrgg_init);