Root/target/linux/ar71xx/files/drivers/mtd/tplinkpart.c

1/*
2 * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation.
7 *
8 */
9
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/slab.h>
13#include <linux/vmalloc.h>
14#include <linux/magic.h>
15
16#include <linux/mtd/mtd.h>
17#include <linux/mtd/partitions.h>
18
19#define TPLINK_NUM_PARTS 5
20#define TPLINK_HEADER_V1 0x01000000
21#define MD5SUM_LEN 16
22
23#define TPLINK_ART_LEN 0x10000
24#define TPLINK_KERNEL_OFFS 0x20000
25
26struct tplink_fw_header {
27    uint32_t version; /* header version */
28    char vendor_name[24];
29    char fw_version[36];
30    uint32_t hw_id; /* hardware id */
31    uint32_t hw_rev; /* hardware revision */
32    uint32_t unk1;
33    uint8_t md5sum1[MD5SUM_LEN];
34    uint32_t unk2;
35    uint8_t md5sum2[MD5SUM_LEN];
36    uint32_t unk3;
37    uint32_t kernel_la; /* kernel load address */
38    uint32_t kernel_ep; /* kernel entry point */
39    uint32_t fw_length; /* total length of the firmware */
40    uint32_t kernel_ofs; /* kernel data offset */
41    uint32_t kernel_len; /* kernel data length */
42    uint32_t rootfs_ofs; /* rootfs data offset */
43    uint32_t rootfs_len; /* rootfs data length */
44    uint32_t boot_ofs; /* bootloader data offset */
45    uint32_t boot_len; /* bootloader data length */
46    uint8_t pad[360];
47} __attribute__ ((packed));
48
49static struct tplink_fw_header *
50tplink_read_header(struct mtd_info *mtd, size_t offset)
51{
52    struct tplink_fw_header *header;
53    size_t header_len;
54    size_t retlen;
55    int ret;
56    u32 t;
57
58    header = vmalloc(sizeof(*header));
59    if (!header)
60        goto err;
61
62    header_len = sizeof(struct tplink_fw_header);
63    ret = mtd_read(mtd, offset, header_len, &retlen,
64               (unsigned char *) header);
65    if (ret)
66        goto err_free_header;
67
68    if (retlen != header_len)
69        goto err_free_header;
70
71    /* sanity checks */
72    t = be32_to_cpu(header->version);
73    if (t != TPLINK_HEADER_V1)
74        goto err_free_header;
75
76    t = be32_to_cpu(header->kernel_ofs);
77    if (t != header_len)
78        goto err_free_header;
79
80    return header;
81
82err_free_header:
83    vfree(header);
84err:
85    return NULL;
86}
87
88static int tplink_check_rootfs_magic(struct mtd_info *mtd, size_t offset)
89{
90    u32 magic;
91    size_t retlen;
92    int ret;
93
94    ret = mtd_read(mtd, offset, sizeof(magic), &retlen,
95               (unsigned char *) &magic);
96    if (ret)
97        return ret;
98
99    if (retlen != sizeof(magic))
100        return -EIO;
101
102    if (le32_to_cpu(magic) != SQUASHFS_MAGIC &&
103        magic != 0x19852003)
104        return -EINVAL;
105
106    return 0;
107}
108
109static int tplink_parse_partitions(struct mtd_info *master,
110                   struct mtd_partition **pparts,
111                   struct mtd_part_parser_data *data)
112{
113    struct mtd_partition *parts;
114    struct tplink_fw_header *header;
115    int nr_parts;
116    size_t offset;
117    size_t art_offset;
118    size_t rootfs_offset;
119    size_t squashfs_offset;
120    int ret;
121
122    nr_parts = TPLINK_NUM_PARTS;
123    parts = kzalloc(nr_parts * sizeof(struct mtd_partition), GFP_KERNEL);
124    if (!parts) {
125        ret = -ENOMEM;
126        goto err;
127    }
128
129    offset = TPLINK_KERNEL_OFFS;
130
131    header = tplink_read_header(master, offset);
132    if (!header) {
133        pr_notice("%s: no TP-Link header found\n", master->name);
134        ret = -ENODEV;
135        goto err_free_parts;
136    }
137
138    squashfs_offset = offset + sizeof(struct tplink_fw_header) +
139              be32_to_cpu(header->kernel_len);
140
141    ret = tplink_check_rootfs_magic(master, squashfs_offset);
142    if (ret == 0)
143        rootfs_offset = squashfs_offset;
144    else
145        rootfs_offset = offset + be32_to_cpu(header->rootfs_ofs);
146
147    art_offset = master->size - TPLINK_ART_LEN;
148
149    parts[0].name = "u-boot";
150    parts[0].offset = 0;
151    parts[0].size = offset;
152    parts[0].mask_flags = MTD_WRITEABLE;
153
154    parts[1].name = "kernel";
155    parts[1].offset = offset;
156    parts[1].size = rootfs_offset - offset;
157
158    parts[2].name = "rootfs";
159    parts[2].offset = rootfs_offset;
160    parts[2].size = art_offset - rootfs_offset;
161
162    parts[3].name = "art";
163    parts[3].offset = art_offset;
164    parts[3].size = TPLINK_ART_LEN;
165    parts[3].mask_flags = MTD_WRITEABLE;
166
167    parts[4].name = "firmware";
168    parts[4].offset = offset;
169    parts[4].size = art_offset - offset;
170
171    vfree(header);
172
173    *pparts = parts;
174    return nr_parts;
175
176err_free_parts:
177    kfree(parts);
178err:
179    *pparts = NULL;
180    return ret;
181}
182
183static struct mtd_part_parser tplink_parser = {
184    .owner = THIS_MODULE,
185    .parse_fn = tplink_parse_partitions,
186    .name = "tp-link",
187};
188
189static int __init tplink_parser_init(void)
190{
191    return register_mtd_parser(&tplink_parser);
192}
193
194module_init(tplink_parser_init);
195
196MODULE_LICENSE("GPL v2");
197MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
198

Archive Download this file



interactive