2 * Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
3 * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published
7 * by the Free Software Foundation.
11 #include <linux/module.h>
12 #include <linux/init.h>
13 #include <linux/kernel.h>
14 #include <linux/slab.h>
15 #include <linux/mtd/mtd.h>
16 #include <linux/mtd/partitions.h>
17 #include <linux/byteorder/generic.h>
21 #define TPLINK_NR_PARTS 2
22 #define TPLINK_MIN_ROOTFS_OFFS 0x80000 /* 512KiB */
29 uint32_t hw_id
; /* hardware id */
30 uint32_t hw_rev
; /* hardware revision */
32 uint8_t md5sum1
[MD5SUM_LEN
];
34 uint8_t md5sum2
[MD5SUM_LEN
];
36 uint32_t kernel_la
; /* kernel load address */
37 uint32_t kernel_ep
; /* kernel entry point */
38 uint32_t fw_length
; /* total length of the firmware */
39 uint32_t kernel_ofs
; /* kernel data offset */
40 uint32_t kernel_len
; /* kernel data length */
41 uint32_t rootfs_ofs
; /* rootfs data offset */
42 uint32_t rootfs_len
; /* rootfs data length */
43 uint32_t boot_ofs
; /* bootloader data offset */
44 uint32_t boot_len
; /* bootloader data length */
46 } __attribute__ ((packed
));
49 char fw_version
[48]; /* 0x04: fw version string */
50 uint32_t hw_id
; /* 0x34: hardware id */
51 uint32_t hw_rev
; /* 0x38: FIXME: hardware revision? */
52 uint32_t unk1
; /* 0x3c: 0x00000000 */
53 uint8_t md5sum1
[MD5SUM_LEN
]; /* 0x40 */
54 uint32_t unk2
; /* 0x50: 0x00000000 */
55 uint8_t md5sum2
[MD5SUM_LEN
]; /* 0x54 */
56 uint32_t unk3
; /* 0x64: 0xffffffff */
58 uint32_t kernel_la
; /* 0x68: kernel load address */
59 uint32_t kernel_ep
; /* 0x6c: kernel entry point */
60 uint32_t fw_length
; /* 0x70: total length of the image */
61 uint32_t kernel_ofs
; /* 0x74: kernel data offset */
62 uint32_t kernel_len
; /* 0x78: kernel data length */
63 uint32_t rootfs_ofs
; /* 0x7c: rootfs data offset */
64 uint32_t rootfs_len
; /* 0x80: rootfs data length */
65 uint32_t boot_ofs
; /* 0x84: FIXME: seems to be unused */
66 uint32_t boot_len
; /* 0x88: FIXME: seems to be unused */
67 uint16_t unk4
; /* 0x8c: 0x55aa */
68 uint8_t sver_hi
; /* 0x8e */
69 uint8_t sver_lo
; /* 0x8f */
70 uint8_t unk5
; /* 0x90: magic: 0xa5 */
71 uint8_t ver_hi
; /* 0x91 */
72 uint8_t ver_mid
; /* 0x92 */
73 uint8_t ver_lo
; /* 0x93 */
75 } __attribute__ ((packed
));
77 struct tplink_fw_header
{
85 static int mtdsplit_parse_tplink(struct mtd_info
*master
,
86 struct mtd_partition
**pparts
,
87 struct mtd_part_parser_data
*data
)
89 struct tplink_fw_header hdr
;
90 size_t hdr_len
, retlen
, kernel_size
;
92 struct mtd_partition
*parts
;
95 hdr_len
= sizeof(hdr
);
96 err
= mtd_read(master
, 0, hdr_len
, &retlen
, (void *) &hdr
);
100 if (retlen
!= hdr_len
)
103 switch (le32_to_cpu(hdr
.version
)) {
105 if (be32_to_cpu(hdr
.v1
.kernel_ofs
) != sizeof(hdr
))
108 kernel_size
= sizeof(hdr
) + be32_to_cpu(hdr
.v1
.kernel_len
);
112 if (be32_to_cpu(hdr
.v2
.kernel_ofs
) != sizeof(hdr
))
115 kernel_size
= sizeof(hdr
) + be32_to_cpu(hdr
.v2
.kernel_len
);
121 if (kernel_size
> master
->size
)
124 /* Find the rootfs after the kernel. */
125 err
= mtd_check_rootfs_magic(master
, kernel_size
, NULL
);
127 rootfs_offset
= kernel_size
;
130 * The size in the header might cover the rootfs as well.
131 * Start the search from an arbitrary offset.
133 err
= mtd_find_rootfs_from(master
, TPLINK_MIN_ROOTFS_OFFS
,
134 master
->size
, &rootfs_offset
, NULL
);
139 parts
= kzalloc(TPLINK_NR_PARTS
* sizeof(*parts
), GFP_KERNEL
);
143 parts
[0].name
= KERNEL_PART_NAME
;
145 parts
[0].size
= rootfs_offset
;
147 parts
[1].name
= ROOTFS_PART_NAME
;
148 parts
[1].offset
= rootfs_offset
;
149 parts
[1].size
= master
->size
- rootfs_offset
;
152 return TPLINK_NR_PARTS
;
155 static struct mtd_part_parser mtdsplit_tplink_parser
= {
156 .owner
= THIS_MODULE
,
158 .parse_fn
= mtdsplit_parse_tplink
,
159 .type
= MTD_PARSER_TYPE_FIRMWARE
,
162 static int __init
mtdsplit_tplink_init(void)
164 register_mtd_parser(&mtdsplit_tplink_parser
);
169 subsys_initcall(mtdsplit_tplink_init
);