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