firmware-utils: tplink-safeloader: add dynamic partitions
[oweals/openwrt.git] / tools / firmware-utils / src / tplink-safeloader.c
1 /*
2   Copyright (c) 2014, Matthias Schiffer <mschiffer@universe-factory.net>
3   All rights reserved.
4
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions are met:
7
8     1. Redistributions of source code must retain the above copyright notice,
9        this list of conditions and the following disclaimer.
10     2. Redistributions in binary form must reproduce the above copyright notice,
11        this list of conditions and the following disclaimer in the documentation
12        and/or other materials provided with the distribution.
13
14   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26
27 /*
28    tplink-safeloader
29
30    Image generation tool for the TP-LINK SafeLoader as seen on
31    TP-LINK Pharos devices (CPE210/220/510/520)
32 */
33
34
35 #include <assert.h>
36 #include <errno.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdint.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44
45 #include <arpa/inet.h>
46
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <limits.h>
50
51 #include "md5.h"
52
53
54 #define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); })
55
56
57 #define MAX_PARTITIONS  32
58
59 /** An image partition table entry */
60 struct image_partition_entry {
61         const char *name;
62         size_t size;
63         uint8_t *data;
64 };
65
66 /** A flash partition table entry */
67 struct flash_partition_entry {
68         char *name;
69         uint32_t base;
70         uint32_t size;
71 };
72
73 /** Firmware layout description */
74 struct device_info {
75         const char *id;
76         const char *vendor;
77         const char *support_list;
78         char support_trail;
79         const char *soft_ver;
80         struct flash_partition_entry partitions[MAX_PARTITIONS+1];
81         const char *first_sysupgrade_partition;
82         const char *last_sysupgrade_partition;
83 };
84
85 /** The content of the soft-version structure */
86 struct __attribute__((__packed__)) soft_version {
87         uint32_t magic;
88         uint32_t zero;
89         uint8_t pad1;
90         uint8_t version_major;
91         uint8_t version_minor;
92         uint8_t version_patch;
93         uint8_t year_hi;
94         uint8_t year_lo;
95         uint8_t month;
96         uint8_t day;
97         uint32_t rev;
98         uint8_t pad2;
99 };
100
101
102 static const uint8_t jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde};
103
104
105 /**
106    Salt for the MD5 hash
107
108    Fortunately, TP-LINK seems to use the same salt for most devices which use
109    the new image format.
110 */
111 static const uint8_t md5_salt[16] = {
112         0x7a, 0x2b, 0x15, 0xed,
113         0x9b, 0x98, 0x59, 0x6d,
114         0xe5, 0x04, 0xab, 0x44,
115         0xac, 0x2a, 0x9f, 0x4e,
116 };
117
118
119 /** Firmware layout table */
120 static struct device_info boards[] = {
121         /** Firmware layout for the CPE210/220 */
122         {
123                 .id     = "CPE210",
124                 .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
125                 .support_list =
126                         "SupportList:\r\n"
127                         "CPE210(TP-LINK|UN|N300-2):1.0\r\n"
128                         "CPE210(TP-LINK|UN|N300-2):1.1\r\n"
129                         "CPE210(TP-LINK|US|N300-2):1.1\r\n"
130                         "CPE210(TP-LINK|EU|N300-2):1.1\r\n"
131                         "CPE220(TP-LINK|UN|N300-2):1.1\r\n"
132                         "CPE220(TP-LINK|US|N300-2):1.1\r\n"
133                         "CPE220(TP-LINK|EU|N300-2):1.1\r\n",
134                 .support_trail = '\xff',
135                 .soft_ver = NULL,
136
137                 .partitions = {
138                         {"fs-uboot", 0x00000, 0x20000},
139                         {"partition-table", 0x20000, 0x02000},
140                         {"default-mac", 0x30000, 0x00020},
141                         {"product-info", 0x31100, 0x00100},
142                         {"signature", 0x32000, 0x00400},
143                         {"os-image", 0x40000, 0x1c0000},
144                         {"file-system", 0x200000, 0x5b0000},
145                         {"soft-version", 0x7b0000, 0x00100},
146                         {"support-list", 0x7b1000, 0x00400},
147                         {"user-config", 0x7c0000, 0x10000},
148                         {"default-config", 0x7d0000, 0x10000},
149                         {"log", 0x7e0000, 0x10000},
150                         {"radio", 0x7f0000, 0x10000},
151                         {NULL, 0, 0}
152                 },
153
154                 .first_sysupgrade_partition = "os-image",
155                 .last_sysupgrade_partition = "support-list",
156         },
157
158         /** Firmware layout for the CPE210 V2 */
159         {
160                 .id     = "CPE210V2",
161                 .vendor = "CPE210(TP-LINK|UN|N300-2|00000000):2.0\r\n",
162                 .support_list =
163                         "SupportList:\r\n"
164                         "CPE210(TP-LINK|EU|N300-2|00000000):2.0\r\n"
165                         "CPE210(TP-LINK|EU|N300-2|45550000):2.0\r\n"
166                         "CPE210(TP-LINK|EU|N300-2|55530000):2.0\r\n"
167                         "CPE210(TP-LINK|UN|N300-2|00000000):2.0\r\n"
168                         "CPE210(TP-LINK|UN|N300-2|45550000):2.0\r\n"
169                         "CPE210(TP-LINK|UN|N300-2|55530000):2.0\r\n"
170                         "CPE210(TP-LINK|US|N300-2|55530000):2.0\r\n"
171                         "CPE210(TP-LINK|UN|N300-2):2.0\r\n"
172                         "CPE210(TP-LINK|EU|N300-2):2.0\r\n"
173                         "CPE210(TP-LINK|US|N300-2):2.0\r\n",
174                 .support_trail = '\xff',
175                 .soft_ver = NULL,
176
177                 .partitions = {
178                         {"fs-uboot", 0x00000, 0x20000},
179                         {"partition-table", 0x20000, 0x02000},
180                         {"default-mac", 0x30000, 0x00020},
181                         {"product-info", 0x31100, 0x00100},
182                         {"device-info", 0x31400, 0x00400},
183                         {"signature", 0x32000, 0x00400},
184                         {"device-id", 0x33000, 0x00100},
185                         {"os-image", 0x40000, 0x1c0000},
186                         {"file-system", 0x200000, 0x5b0000},
187                         {"soft-version", 0x7b0000, 0x00100},
188                         {"support-list", 0x7b1000, 0x01000},
189                         {"user-config", 0x7c0000, 0x10000},
190                         {"default-config", 0x7d0000, 0x10000},
191                         {"log", 0x7e0000, 0x10000},
192                         {"radio", 0x7f0000, 0x10000},
193                         {NULL, 0, 0}
194                 },
195
196                 .first_sysupgrade_partition = "os-image",
197                 .last_sysupgrade_partition = "support-list",
198         },
199
200         /** Firmware layout for the CPE510/520 */
201         {
202                 .id     = "CPE510",
203                 .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
204                 .support_list =
205                         "SupportList:\r\n"
206                         "CPE510(TP-LINK|UN|N300-5):1.0\r\n"
207                         "CPE510(TP-LINK|UN|N300-5):1.1\r\n"
208                         "CPE510(TP-LINK|UN|N300-5):1.1\r\n"
209                         "CPE510(TP-LINK|US|N300-5):1.1\r\n"
210                         "CPE510(TP-LINK|EU|N300-5):1.1\r\n"
211                         "CPE520(TP-LINK|UN|N300-5):1.1\r\n"
212                         "CPE520(TP-LINK|US|N300-5):1.1\r\n"
213                         "CPE520(TP-LINK|EU|N300-5):1.1\r\n",
214                 .support_trail = '\xff',
215                 .soft_ver = NULL,
216
217                 .partitions = {
218                         {"fs-uboot", 0x00000, 0x20000},
219                         {"partition-table", 0x20000, 0x02000},
220                         {"default-mac", 0x30000, 0x00020},
221                         {"product-info", 0x31100, 0x00100},
222                         {"signature", 0x32000, 0x00400},
223                         {"os-image", 0x40000, 0x1c0000},
224                         {"file-system", 0x200000, 0x5b0000},
225                         {"soft-version", 0x7b0000, 0x00100},
226                         {"support-list", 0x7b1000, 0x00400},
227                         {"user-config", 0x7c0000, 0x10000},
228                         {"default-config", 0x7d0000, 0x10000},
229                         {"log", 0x7e0000, 0x10000},
230                         {"radio", 0x7f0000, 0x10000},
231                         {NULL, 0, 0}
232                 },
233
234                 .first_sysupgrade_partition = "os-image",
235                 .last_sysupgrade_partition = "support-list",
236         },
237
238         {
239                 .id     = "WBS210",
240                 .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
241                 .support_list =
242                         "SupportList:\r\n"
243                         "WBS210(TP-LINK|UN|N300-2):1.20\r\n"
244                         "WBS210(TP-LINK|US|N300-2):1.20\r\n"
245                         "WBS210(TP-LINK|EU|N300-2):1.20\r\n",
246                 .support_trail = '\xff',
247                 .soft_ver = NULL,
248
249                 .partitions = {
250                         {"fs-uboot", 0x00000, 0x20000},
251                         {"partition-table", 0x20000, 0x02000},
252                         {"default-mac", 0x30000, 0x00020},
253                         {"product-info", 0x31100, 0x00100},
254                         {"signature", 0x32000, 0x00400},
255                         {"os-image", 0x40000, 0x1c0000},
256                         {"file-system", 0x200000, 0x5b0000},
257                         {"soft-version", 0x7b0000, 0x00100},
258                         {"support-list", 0x7b1000, 0x00400},
259                         {"user-config", 0x7c0000, 0x10000},
260                         {"default-config", 0x7d0000, 0x10000},
261                         {"log", 0x7e0000, 0x10000},
262                         {"radio", 0x7f0000, 0x10000},
263                         {NULL, 0, 0}
264                 },
265
266                 .first_sysupgrade_partition = "os-image",
267                 .last_sysupgrade_partition = "support-list",
268         },
269
270         {
271                 .id     = "WBS510",
272                 .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n",
273                 .support_list =
274                         "SupportList:\r\n"
275                         "WBS510(TP-LINK|UN|N300-5):1.20\r\n"
276                         "WBS510(TP-LINK|US|N300-5):1.20\r\n"
277                         "WBS510(TP-LINK|EU|N300-5):1.20\r\n",
278                 .support_trail = '\xff',
279                 .soft_ver = NULL,
280
281                 .partitions = {
282                         {"fs-uboot", 0x00000, 0x20000},
283                         {"partition-table", 0x20000, 0x02000},
284                         {"default-mac", 0x30000, 0x00020},
285                         {"product-info", 0x31100, 0x00100},
286                         {"signature", 0x32000, 0x00400},
287                         {"os-image", 0x40000, 0x1c0000},
288                         {"file-system", 0x200000, 0x5b0000},
289                         {"soft-version", 0x7b0000, 0x00100},
290                         {"support-list", 0x7b1000, 0x00400},
291                         {"user-config", 0x7c0000, 0x10000},
292                         {"default-config", 0x7d0000, 0x10000},
293                         {"log", 0x7e0000, 0x10000},
294                         {"radio", 0x7f0000, 0x10000},
295                         {NULL, 0, 0}
296                 },
297
298                 .first_sysupgrade_partition = "os-image",
299                 .last_sysupgrade_partition = "support-list",
300         },
301
302         /** Firmware layout for the C2600 */
303         {
304                 .id = "C2600",
305                 .vendor = "",
306                 .support_list =
307                         "SupportList:\r\n"
308                         "{product_name:Archer C2600,product_ver:1.0.0,special_id:00000000}\r\n",
309                 .support_trail = '\x00',
310                 .soft_ver = NULL,
311
312                 /**
313                     We use a bigger os-image partition than the stock images (and thus
314                     smaller file-system), as our kernel doesn't fit in the stock firmware's
315                     2 MB os-image since kernel 4.14.
316                 */
317                 .partitions = {
318                         {"SBL1", 0x00000, 0x20000},
319                         {"MIBIB", 0x20000, 0x20000},
320                         {"SBL2", 0x40000, 0x20000},
321                         {"SBL3", 0x60000, 0x30000},
322                         {"DDRCONFIG", 0x90000, 0x10000},
323                         {"SSD", 0xa0000, 0x10000},
324                         {"TZ", 0xb0000, 0x30000},
325                         {"RPM", 0xe0000, 0x20000},
326                         {"fs-uboot", 0x100000, 0x70000},
327                         {"uboot-env", 0x170000, 0x40000},
328                         {"radio", 0x1b0000, 0x40000},
329                         {"os-image", 0x1f0000, 0x400000}, /* Stock: base 0x1f0000 size 0x200000 */
330                         {"file-system", 0x5f0000, 0x1900000}, /* Stock: base 0x3f0000 size 0x1b00000 */
331                         {"default-mac", 0x1ef0000, 0x00200},
332                         {"pin", 0x1ef0200, 0x00200},
333                         {"product-info", 0x1ef0400, 0x0fc00},
334                         {"partition-table", 0x1f00000, 0x10000},
335                         {"soft-version", 0x1f10000, 0x10000},
336                         {"support-list", 0x1f20000, 0x10000},
337                         {"profile", 0x1f30000, 0x10000},
338                         {"default-config", 0x1f40000, 0x10000},
339                         {"user-config", 0x1f50000, 0x40000},
340                         {"qos-db", 0x1f90000, 0x40000},
341                         {"usb-config", 0x1fd0000, 0x10000},
342                         {"log", 0x1fe0000, 0x20000},
343                         {NULL, 0, 0}
344                 },
345
346                 .first_sysupgrade_partition = "os-image",
347                 .last_sysupgrade_partition = "file-system"
348         },
349
350         /** Firmware layout for the C25v1 */
351         {
352                 .id = "ARCHER-C25-V1",
353                 .support_list =
354                         "SupportList:\n"
355                         "{product_name:ArcherC25,product_ver:1.0.0,special_id:00000000}\n"
356                         "{product_name:ArcherC25,product_ver:1.0.0,special_id:55530000}\n"
357                         "{product_name:ArcherC25,product_ver:1.0.0,special_id:45550000}\n",
358                 .support_trail = '\x00',
359                 .soft_ver = "soft_ver:1.0.0\n",
360
361                 /**
362                     We use a bigger os-image partition than the stock images (and thus
363                     smaller file-system), as our kernel doesn't fit in the stock firmware's
364                     1MB os-image.
365                 */
366                 .partitions = {
367                         {"factory-boot", 0x00000, 0x20000},
368                         {"fs-uboot", 0x20000, 0x10000},
369                         {"os-image", 0x30000, 0x180000},        /* Stock: base 0x30000 size 0x100000 */
370                         {"file-system", 0x1b0000, 0x620000},    /* Stock: base 0x130000 size 0x6a0000 */
371                         {"user-config", 0x7d0000, 0x04000},
372                         {"default-mac", 0x7e0000, 0x00100},
373                         {"device-id", 0x7e0100, 0x00100},
374                         {"extra-para", 0x7e0200, 0x00100},
375                         {"pin", 0x7e0300, 0x00100},
376                         {"support-list", 0x7e0400, 0x00400},
377                         {"soft-version", 0x7e0800, 0x00400},
378                         {"product-info", 0x7e0c00, 0x01400},
379                         {"partition-table", 0x7e2000, 0x01000},
380                         {"profile", 0x7e3000, 0x01000},
381                         {"default-config", 0x7e4000, 0x04000},
382                         {"merge-config", 0x7ec000, 0x02000},
383                         {"qos-db", 0x7ee000, 0x02000},
384                         {"radio", 0x7f0000, 0x10000},
385                         {NULL, 0, 0}
386                 },
387
388                 .first_sysupgrade_partition = "os-image",
389                 .last_sysupgrade_partition = "file-system",
390         },
391
392         /** Firmware layout for the C58v1 */
393         {
394                 .id     = "ARCHER-C58-V1",
395                 .vendor = "",
396                 .support_list =
397                         "SupportList:\r\n"
398                         "{product_name:Archer C58,product_ver:1.0.0,special_id:00000000}\r\n"
399                         "{product_name:Archer C58,product_ver:1.0.0,special_id:45550000}\r\n"
400                         "{product_name:Archer C58,product_ver:1.0.0,special_id:55530000}\r\n",
401                 .support_trail = '\x00',
402                 .soft_ver = "soft_ver:1.0.0\n",
403
404                 .partitions = {
405                         {"fs-uboot", 0x00000, 0x10000},
406                         {"default-mac", 0x10000, 0x00200},
407                         {"pin", 0x10200, 0x00200},
408                         {"product-info", 0x10400, 0x00100},
409                         {"partition-table", 0x10500, 0x00800},
410                         {"soft-version", 0x11300, 0x00200},
411                         {"support-list", 0x11500, 0x00100},
412                         {"device-id", 0x11600, 0x00100},
413                         {"profile", 0x11700, 0x03900},
414                         {"default-config", 0x15000, 0x04000},
415                         {"user-config", 0x19000, 0x04000},
416                         {"os-image", 0x20000, 0x180000},
417                         {"file-system", 0x1a0000, 0x648000},
418                         {"certyficate", 0x7e8000, 0x08000},
419                         {"radio", 0x7f0000, 0x10000},
420                         {NULL, 0, 0}
421                 },
422
423                 .first_sysupgrade_partition = "os-image",
424                 .last_sysupgrade_partition = "file-system",
425         },
426
427         /** Firmware layout for the C59v1 */
428         {
429                 .id     = "ARCHER-C59-V1",
430                 .vendor = "",
431                 .support_list =
432                         "SupportList:\r\n"
433                         "{product_name:Archer C59,product_ver:1.0.0,special_id:00000000}\r\n"
434                         "{product_name:Archer C59,product_ver:1.0.0,special_id:45550000}\r\n"
435                         "{product_name:Archer C59,product_ver:1.0.0,special_id:52550000}\r\n"
436                         "{product_name:Archer C59,product_ver:1.0.0,special_id:55530000}\r\n",
437                 .support_trail = '\x00',
438                 .soft_ver = "soft_ver:1.0.0\n",
439
440                 .partitions = {
441                         {"fs-uboot", 0x00000, 0x10000},
442                         {"default-mac", 0x10000, 0x00200},
443                         {"pin", 0x10200, 0x00200},
444                         {"device-id", 0x10400, 0x00100},
445                         {"product-info", 0x10500, 0x0fb00},
446                         {"os-image", 0x20000, 0x180000},
447                         {"file-system", 0x1a0000, 0xcb0000},
448                         {"partition-table", 0xe50000, 0x10000},
449                         {"soft-version", 0xe60000, 0x10000},
450                         {"support-list", 0xe70000, 0x10000},
451                         {"profile", 0xe80000, 0x10000},
452                         {"default-config", 0xe90000, 0x10000},
453                         {"user-config", 0xea0000, 0x40000},
454                         {"usb-config", 0xee0000, 0x10000},
455                         {"certificate", 0xef0000, 0x10000},
456                         {"qos-db", 0xf00000, 0x40000},
457                         {"log", 0xfe0000, 0x10000},
458                         {"radio", 0xff0000, 0x10000},
459                         {NULL, 0, 0}
460                 },
461
462                 .first_sysupgrade_partition = "os-image",
463                 .last_sysupgrade_partition = "file-system",
464         },
465
466         /** Firmware layout for the C60v1 */
467         {
468                 .id     = "ARCHER-C60-V1",
469                 .vendor = "",
470                 .support_list =
471                         "SupportList:\r\n"
472                         "{product_name:Archer C60,product_ver:1.0.0,special_id:00000000}\r\n"
473                         "{product_name:Archer C60,product_ver:1.0.0,special_id:45550000}\r\n"
474                         "{product_name:Archer C60,product_ver:1.0.0,special_id:55530000}\r\n",
475                 .support_trail = '\x00',
476                 .soft_ver = "soft_ver:1.0.0\n",
477
478                 .partitions = {
479                         {"fs-uboot", 0x00000, 0x10000},
480                         {"default-mac", 0x10000, 0x00200},
481                         {"pin", 0x10200, 0x00200},
482                         {"product-info", 0x10400, 0x00100},
483                         {"partition-table", 0x10500, 0x00800},
484                         {"soft-version", 0x11300, 0x00200},
485                         {"support-list", 0x11500, 0x00100},
486                         {"device-id", 0x11600, 0x00100},
487                         {"profile", 0x11700, 0x03900},
488                         {"default-config", 0x15000, 0x04000},
489                         {"user-config", 0x19000, 0x04000},
490                         {"os-image", 0x20000, 0x180000},
491                         {"file-system", 0x1a0000, 0x648000},
492                         {"certyficate", 0x7e8000, 0x08000},
493                         {"radio", 0x7f0000, 0x10000},
494                         {NULL, 0, 0}
495                 },
496
497                 .first_sysupgrade_partition = "os-image",
498                 .last_sysupgrade_partition = "file-system",
499         },
500
501         /** Firmware layout for the C60v2 */
502         {
503                 .id     = "ARCHER-C60-V2",
504                 .vendor = "",
505                 .support_list =
506                         "SupportList:\r\n"
507                         "{product_name:Archer C60,product_ver:2.0.0,special_id:42520000}\r\n"
508                         "{product_name:Archer C60,product_ver:2.0.0,special_id:45550000}\r\n"
509                         "{product_name:Archer C60,product_ver:2.0.0,special_id:55530000}\r\n",
510                 .support_trail = '\x00',
511                 .soft_ver = "soft_ver:2.0.0\n",
512
513                 .partitions = {
514                         {"factory-boot", 0x00000, 0x1fb00},
515                         {"default-mac", 0x1fb00, 0x00200},
516                         {"pin", 0x1fd00, 0x00100},
517                         {"product-info", 0x1fe00, 0x00100},
518                         {"device-id", 0x1ff00, 0x00100},
519                         {"fs-uboot", 0x20000, 0x10000},
520                         {"os-image", 0x30000, 0x180000},
521                         {"file-system", 0x1b0000, 0x620000},
522                         {"soft-version", 0x7d9500, 0x00100},
523                         {"support-list", 0x7d9600, 0x00100},
524                         {"extra-para", 0x7d9700, 0x00100},
525                         {"profile", 0x7d9800, 0x03000},
526                         {"default-config", 0x7dc800, 0x03000},
527                         {"partition-table", 0x7df800, 0x00800},
528                         {"user-config", 0x7e0000, 0x0c000},
529                         {"certificate", 0x7ec000, 0x04000},
530                         {"radio", 0x7f0000, 0x10000},
531                         {NULL, 0, 0}
532                 },
533
534                 .first_sysupgrade_partition = "os-image",
535                 .last_sysupgrade_partition = "file-system",
536         },
537
538         /** Firmware layout for the C5 */
539         {
540                 .id = "ARCHER-C5-V2",
541                 .vendor = "",
542                 .support_list =
543                         "SupportList:\r\n"
544                         "{product_name:ArcherC5,product_ver:2.0.0,special_id:00000000}\r\n"
545                         "{product_name:ArcherC5,product_ver:2.0.0,special_id:55530000}\r\n"
546                         "{product_name:ArcherC5,product_ver:2.0.0,special_id:4A500000}\r\n", /* JP version */
547                 .support_trail = '\x00',
548                 .soft_ver = NULL,
549
550                 .partitions = {
551                         {"fs-uboot", 0x00000, 0x40000},
552                         {"os-image", 0x40000, 0x200000},
553                         {"file-system", 0x240000, 0xc00000},
554                         {"default-mac", 0xe40000, 0x00200},
555                         {"pin", 0xe40200, 0x00200},
556                         {"product-info", 0xe40400, 0x00200},
557                         {"partition-table", 0xe50000, 0x10000},
558                         {"soft-version", 0xe60000, 0x00200},
559                         {"support-list", 0xe61000, 0x0f000},
560                         {"profile", 0xe70000, 0x10000},
561                         {"default-config", 0xe80000, 0x10000},
562                         {"user-config", 0xe90000, 0x50000},
563                         {"log", 0xee0000, 0x100000},
564                         {"radio_bk", 0xfe0000, 0x10000},
565                         {"radio", 0xff0000, 0x10000},
566                         {NULL, 0, 0}
567                 },
568
569                 .first_sysupgrade_partition = "os-image",
570                 .last_sysupgrade_partition = "file-system"
571         },
572
573         /** Firmware layout for the C7 */
574         {
575                 .id = "ARCHER-C7-V4",
576                 .support_list =
577                         "SupportList:\n"
578                         "{product_name:Archer C7,product_ver:4.0.0,special_id:00000000}\n"
579                         "{product_name:Archer C7,product_ver:4.0.0,special_id:41550000}\n"
580                         "{product_name:Archer C7,product_ver:4.0.0,special_id:45550000}\n"
581                         "{product_name:Archer C7,product_ver:4.0.0,special_id:4B520000}\n"
582                         "{product_name:Archer C7,product_ver:4.0.0,special_id:42520000}\n"
583                         "{product_name:Archer C7,product_ver:4.0.0,special_id:4A500000}\n"
584                         "{product_name:Archer C7,product_ver:4.0.0,special_id:52550000}\n"
585                         "{product_name:Archer C7,product_ver:4.0.0,special_id:54570000}\n"
586                         "{product_name:Archer C7,product_ver:4.0.0,special_id:55530000}\n"
587                         "{product_name:Archer C7,product_ver:4.0.0,special_id:43410000}\n",
588                 .support_trail = '\x00',
589                 .soft_ver = "soft_ver:1.0.0\n",
590
591                 /**
592                     We use a bigger os-image partition than the stock images (and thus
593                     smaller file-system), as our kernel doesn't fit in the stock firmware's
594                     1MB os-image.
595                 */
596                 .partitions = {
597                         {"factory-boot", 0x00000, 0x20000},
598                         {"fs-uboot", 0x20000, 0x20000},
599                         {"os-image", 0x40000, 0x180000},        /* Stock: base 0x40000 size 0x120000 */
600                         {"file-system", 0x1c0000, 0xd40000},    /* Stock: base 0x160000 size 0xda0000 */
601                         {"default-mac", 0xf00000, 0x00200},
602                         {"pin", 0xf00200, 0x00200},
603                         {"device-id", 0xf00400, 0x00100},
604                         {"product-info", 0xf00500, 0x0fb00},
605                         {"soft-version", 0xf10000, 0x00100},
606                         {"extra-para", 0xf11000, 0x01000},
607                         {"support-list", 0xf12000, 0x0a000},
608                         {"profile", 0xf1c000, 0x04000},
609                         {"default-config", 0xf20000, 0x10000},
610                         {"user-config", 0xf30000, 0x40000},
611                         {"qos-db", 0xf70000, 0x40000},
612                         {"certificate", 0xfb0000, 0x10000},
613                         {"partition-table", 0xfc0000, 0x10000},
614                         {"log", 0xfd0000, 0x20000},
615                         {"radio", 0xff0000, 0x10000},
616                         {NULL, 0, 0}
617                 },
618
619                 .first_sysupgrade_partition = "os-image",
620                 .last_sysupgrade_partition = "file-system",
621         },
622
623         /** Firmware layout for the C7 v5*/
624         {
625                 .id = "ARCHER-C7-V5",
626                 .support_list =
627                         "SupportList:\n"
628                         "{product_name:Archer C7,product_ver:5.0.0,special_id:00000000}\n"
629                         "{product_name:Archer C7,product_ver:5.0.0,special_id:55530000}\n",
630
631                 .support_trail = '\x00',
632                 .soft_ver = "soft_ver:1.0.0\n",
633
634                 /**
635                   We use a bigger os-image partition than the stock images (and thus
636                   smaller file-system), as our kernel doesn't fit in the stock firmware's
637                   1MB os-image.
638                   */
639                 .partitions = {
640                         {"factory-boot",    0x00000,  0x20000},
641                         {"fs-uboot",        0x20000,  0x20000},
642                         {"partition-table", 0x40000,  0x10000},
643                         {"radio",           0x50000,  0x10000},
644                         {"default-mac",     0x60000,  0x00200},
645                         {"pin",             0x60200,  0x00200},
646                         {"device-id",       0x60400,  0x00100},
647                         {"product-info",    0x60500,  0x0fb00},
648                         {"soft-version",    0x70000,  0x01000},
649                         {"extra-para",      0x71000,  0x01000},
650                         {"support-list",    0x72000,  0x0a000},
651                         {"profile",         0x7c000,  0x04000},
652                         {"user-config",     0x80000,  0x40000},
653
654
655                         {"os-image",        0xc0000,  0x180000}, /* Stock: base 0xc0000  size 0x120000 */
656                         {"file-system",     0x240000, 0xd80000}, /* Stock: base 0x1e0000 size 0xde0000 */
657
658                         {"log",             0xfc0000, 0x20000},
659                         {"certificate",     0xfe0000, 0x10000},
660                         {"default-config",  0xff0000, 0x10000},
661                         {NULL, 0, 0}
662
663                 },
664
665                 .first_sysupgrade_partition = "os-image",
666                 .last_sysupgrade_partition = "file-system",
667         },
668
669         /** Firmware layout for the C9 */
670         {
671                 .id = "ARCHERC9",
672                 .vendor = "",
673                 .support_list =
674                         "SupportList:\n"
675                         "{product_name:ArcherC9,"
676                         "product_ver:1.0.0,"
677                         "special_id:00000000}\n",
678                 .support_trail = '\x00',
679                 .soft_ver = NULL,
680
681                 .partitions = {
682                         {"fs-uboot", 0x00000, 0x40000},
683                         {"os-image", 0x40000, 0x200000},
684                         {"file-system", 0x240000, 0xc00000},
685                         {"default-mac", 0xe40000, 0x00200},
686                         {"pin", 0xe40200, 0x00200},
687                         {"product-info", 0xe40400, 0x00200},
688                         {"partition-table", 0xe50000, 0x10000},
689                         {"soft-version", 0xe60000, 0x00200},
690                         {"support-list", 0xe61000, 0x0f000},
691                         {"profile", 0xe70000, 0x10000},
692                         {"default-config", 0xe80000, 0x10000},
693                         {"user-config", 0xe90000, 0x50000},
694                         {"log", 0xee0000, 0x100000},
695                         {"radio_bk", 0xfe0000, 0x10000},
696                         {"radio", 0xff0000, 0x10000},
697                         {NULL, 0, 0}
698                 },
699
700                 .first_sysupgrade_partition = "os-image",
701                 .last_sysupgrade_partition = "file-system"
702         },
703
704         /** Firmware layout for the EAP120 */
705         {
706                 .id     = "EAP120",
707                 .vendor = "EAP120(TP-LINK|UN|N300-2):1.0\r\n",
708                 .support_list =
709                         "SupportList:\r\n"
710                         "EAP120(TP-LINK|UN|N300-2):1.0\r\n",
711                 .support_trail = '\xff',
712                 .soft_ver = NULL,
713
714                 .partitions = {
715                         {"fs-uboot", 0x00000, 0x20000},
716                         {"partition-table", 0x20000, 0x02000},
717                         {"default-mac", 0x30000, 0x00020},
718                         {"support-list", 0x31000, 0x00100},
719                         {"product-info", 0x31100, 0x00100},
720                         {"soft-version", 0x32000, 0x00100},
721                         {"os-image", 0x40000, 0x180000},
722                         {"file-system", 0x1c0000, 0x600000},
723                         {"user-config", 0x7c0000, 0x10000},
724                         {"backup-config", 0x7d0000, 0x10000},
725                         {"log", 0x7e0000, 0x10000},
726                         {"radio", 0x7f0000, 0x10000},
727                         {NULL, 0, 0}
728                 },
729
730                 .first_sysupgrade_partition = "os-image",
731                 .last_sysupgrade_partition = "file-system"
732         },
733
734         /** Firmware layout for the TL-WA850RE v2 */
735         {
736                 .id     = "TLWA850REV2",
737                 .vendor = "",
738                 .support_list =
739                         "SupportList:\n"
740                         "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:55530000}\n"
741                         "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:00000000}\n"
742                         "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:55534100}\n"
743                         "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:45550000}\n"
744                         "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:4B520000}\n"
745                         "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:42520000}\n"
746                         "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:4A500000}\n"
747                         "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:43410000}\n"
748                         "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:41550000}\n"
749                         "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:52550000}\n",
750                 .support_trail = '\x00',
751                 .soft_ver = NULL,
752
753                 /**
754                    576KB were moved from file-system to os-image
755                    in comparison to the stock image
756                 */
757                 .partitions = {
758                         {"fs-uboot", 0x00000, 0x20000},
759                         {"os-image", 0x20000, 0x150000},
760                         {"file-system", 0x170000, 0x240000},
761                         {"partition-table", 0x3b0000, 0x02000},
762                         {"default-mac", 0x3c0000, 0x00020},
763                         {"pin", 0x3c0100, 0x00020},
764                         {"product-info", 0x3c1000, 0x01000},
765                         {"soft-version", 0x3c2000, 0x00100},
766                         {"support-list", 0x3c3000, 0x01000},
767                         {"profile", 0x3c4000, 0x08000},
768                         {"user-config", 0x3d0000, 0x10000},
769                         {"default-config", 0x3e0000, 0x10000},
770                         {"radio", 0x3f0000, 0x10000},
771                         {NULL, 0, 0}
772                 },
773
774                 .first_sysupgrade_partition = "os-image",
775                 .last_sysupgrade_partition = "file-system"
776         },
777
778         /** Firmware layout for the TL-WA855RE v1 */
779         {
780                 .id     = "TLWA855REV1",
781                 .vendor = "",
782                 .support_list =
783                         "SupportList:\n"
784                         "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:00000000}\n"
785                         "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:55530000}\n"
786                         "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:45550000}\n"
787                         "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:4B520000}\n"
788                         "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:42520000}\n"
789                         "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:4A500000}\n"
790                         "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:43410000}\n"
791                         "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:41550000}\n"
792                         "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:52550000}\n",
793                 .support_trail = '\x00',
794                 .soft_ver = NULL,
795
796                 .partitions = {
797                         {"fs-uboot", 0x00000, 0x20000},
798                         {"os-image", 0x20000, 0x150000},
799                         {"file-system", 0x170000, 0x240000},
800                         {"partition-table", 0x3b0000, 0x02000},
801                         {"default-mac", 0x3c0000, 0x00020},
802                         {"pin", 0x3c0100, 0x00020},
803                         {"product-info", 0x3c1000, 0x01000},
804                         {"soft-version", 0x3c2000, 0x00100},
805                         {"support-list", 0x3c3000, 0x01000},
806                         {"profile", 0x3c4000, 0x08000},
807                         {"user-config", 0x3d0000, 0x10000},
808                         {"default-config", 0x3e0000, 0x10000},
809                         {"radio", 0x3f0000, 0x10000},
810                         {NULL, 0, 0}
811                 },
812
813                 .first_sysupgrade_partition = "os-image",
814                 .last_sysupgrade_partition = "file-system"
815         },
816
817         /** Firmware layout for the TL-WR1043 v5 */
818         {
819                 .id     = "TLWR1043NV5",
820                 .vendor = "",
821                 .support_list =
822                         "SupportList:\n"
823                         "{product_name:TL-WR1043N,product_ver:5.0.0,special_id:45550000}\n"
824                         "{product_name:TL-WR1043N,product_ver:5.0.0,special_id:55530000}\n",
825                 .support_trail = '\x00',
826                 .soft_ver = "soft_ver:1.0.0\n",
827                 .partitions = {
828                         {"factory-boot", 0x00000, 0x20000},
829                         {"fs-uboot", 0x20000, 0x20000},
830                         {"os-image", 0x40000, 0x180000},
831                         {"file-system", 0x1c0000, 0xd40000},
832                         {"default-mac", 0xf00000, 0x00200},
833                         {"pin", 0xf00200, 0x00200},
834                         {"device-id", 0xf00400, 0x00100},
835                         {"product-info", 0xf00500, 0x0fb00},
836                         {"soft-version", 0xf10000, 0x01000},
837                         {"extra-para", 0xf11000, 0x01000},
838                         {"support-list", 0xf12000, 0x0a000},
839                         {"profile", 0xf1c000, 0x04000},
840                         {"default-config", 0xf20000, 0x10000},
841                         {"user-config", 0xf30000, 0x40000},
842                         {"qos-db", 0xf70000, 0x40000},
843                         {"certificate", 0xfb0000, 0x10000},
844                         {"partition-table", 0xfc0000, 0x10000},
845                         {"log", 0xfd0000, 0x20000},
846                         {"radio", 0xff0000, 0x10000},
847                         {NULL, 0, 0}
848                 },
849                 .first_sysupgrade_partition = "os-image",
850                 .last_sysupgrade_partition = "file-system"
851         },
852
853         /** Firmware layout for the TL-WR1043 v4 */
854         {
855                 .id     = "TLWR1043NDV4",
856                 .vendor = "",
857                 .support_list =
858                         "SupportList:\n"
859                         "{product_name:TL-WR1043ND,product_ver:4.0.0,special_id:45550000}\n",
860                 .support_trail = '\x00',
861                 .soft_ver = NULL,
862
863                 /**
864                     We use a bigger os-image partition than the stock images (and thus
865                     smaller file-system), as our kernel doesn't fit in the stock firmware's
866                     1MB os-image.
867                 */
868                 .partitions = {
869                         {"fs-uboot", 0x00000, 0x20000},
870                         {"os-image", 0x20000, 0x200000},
871                         {"file-system", 0x220000, 0xd30000},
872                         {"default-mac", 0xf50000, 0x00200},
873                         {"pin", 0xf50200, 0x00200},
874                         {"product-info", 0xf50400, 0x0fc00},
875                         {"soft-version", 0xf60000, 0x0b000},
876                         {"support-list", 0xf6b000, 0x04000},
877                         {"profile", 0xf70000, 0x04000},
878                         {"default-config", 0xf74000, 0x0b000},
879                         {"user-config", 0xf80000, 0x40000},
880                         {"partition-table", 0xfc0000, 0x10000},
881                         {"log", 0xfd0000, 0x20000},
882                         {"radio", 0xff0000, 0x10000},
883                         {NULL, 0, 0}
884                 },
885
886                 .first_sysupgrade_partition = "os-image",
887                 .last_sysupgrade_partition = "file-system"
888         },
889
890         /** Firmware layout for the TL-WR902AC v1 */
891         {
892                 .id     = "TL-WR902AC-V1",
893                 .vendor = "",
894                 .support_list =
895                         "SupportList:\n"
896                         "{product_name:TL-WR902AC,product_ver:1.0.0,special_id:45550000}\n"
897                         "{product_name:TL-WR902AC,product_ver:1.0.0,special_id:55530000}\n",
898                 .support_trail = '\x00',
899                 .soft_ver = NULL,
900
901                 /**
902                    384KB were moved from file-system to os-image
903                    in comparison to the stock image
904                 */
905                 .partitions = {
906                         {"fs-uboot", 0x00000, 0x20000},
907                         {"os-image", 0x20000, 0x180000},
908                         {"file-system", 0x1a0000, 0x5b0000},
909                         {"default-mac", 0x750000, 0x00200},
910                         {"pin", 0x750200, 0x00200},
911                         {"product-info", 0x750400, 0x0fc00},
912                         {"soft-version", 0x760000, 0x0b000},
913                         {"support-list", 0x76b000, 0x04000},
914                         {"profile", 0x770000, 0x04000},
915                         {"default-config", 0x774000, 0x0b000},
916                         {"user-config", 0x780000, 0x40000},
917                         {"partition-table", 0x7c0000, 0x10000},
918                         {"log", 0x7d0000, 0x20000},
919                         {"radio", 0x7f0000, 0x10000},
920                         {NULL, 0, 0}
921                 },
922
923                 .first_sysupgrade_partition = "os-image",
924                 .last_sysupgrade_partition = "file-system",
925         },
926
927         /** Firmware layout for the TL-WR942N V1 */
928         {
929                 .id     = "TLWR942NV1",
930                 .vendor = "",
931                 .support_list =
932                         "SupportList:\r\n"
933                         "{product_name:TL-WR942N,product_ver:1.0.0,special_id:00000000}\r\n"
934                         "{product_name:TL-WR942N,product_ver:1.0.0,special_id:52550000}\r\n",
935                 .support_trail = '\x00',
936                 .soft_ver = NULL,
937
938                 .partitions = {
939                         {"fs-uboot", 0x00000, 0x20000},
940                         {"os-image", 0x20000, 0x180000},
941                         {"file-system", 0x1a0000, 0xca0000},
942                         {"default-mac", 0xe40000, 0x00200},
943                         {"pin", 0xe40200, 0x00200},
944                         {"product-info", 0xe40400, 0x0fc00},
945                         {"partition-table", 0xe50000, 0x10000},
946                         {"soft-version", 0xe60000, 0x10000},
947                         {"support-list", 0xe70000, 0x10000},
948                         {"profile", 0xe80000, 0x10000},
949                         {"default-config", 0xe90000, 0x10000},
950                         {"user-config", 0xea0000, 0x40000},
951                         {"qos-db", 0xee0000, 0x40000},
952                         {"certificate", 0xf20000, 0x10000},
953                         {"usb-config", 0xfb0000, 0x10000},
954                         {"log", 0xfc0000, 0x20000},
955                         {"radio-bk", 0xfe0000, 0x10000},
956                         {"radio", 0xff0000, 0x10000},
957                         {NULL, 0, 0}
958                 },
959
960                 .first_sysupgrade_partition = "os-image",
961                 .last_sysupgrade_partition = "file-system",
962         },
963
964         /** Firmware layout for the RE350 v1 */
965         {
966                 .id = "RE350-V1",
967                 .vendor = "",
968                 .support_list =
969                         "SupportList:\n"
970                         "{product_name:RE350,product_ver:1.0.0,special_id:45550000}\n"
971                         "{product_name:RE350,product_ver:1.0.0,special_id:00000000}\n"
972                         "{product_name:RE350,product_ver:1.0.0,special_id:41550000}\n"
973                         "{product_name:RE350,product_ver:1.0.0,special_id:55530000}\n"
974                         "{product_name:RE350,product_ver:1.0.0,special_id:43410000}\n"
975                         "{product_name:RE350,product_ver:1.0.0,special_id:4b520000}\n"
976                         "{product_name:RE350,product_ver:1.0.0,special_id:4a500000}\n",
977                 .support_trail = '\x00',
978                 .soft_ver = NULL,
979
980                 /**
981                         The original os-image partition is too small,
982                         so we enlarge it to 1.75M
983                 */
984                 .partitions = {
985                         {"fs-uboot", 0x00000, 0x20000},
986                         {"os-image", 0x20000, 0x1c0000},
987                         {"file-system", 0x1e0000, 0x420000},
988                         {"partition-table", 0x600000, 0x02000},
989                         {"default-mac", 0x610000, 0x00020},
990                         {"pin", 0x610100, 0x00020},
991                         {"product-info", 0x611100, 0x01000},
992                         {"soft-version", 0x620000, 0x01000},
993                         {"support-list", 0x621000, 0x01000},
994                         {"profile", 0x622000, 0x08000},
995                         {"user-config", 0x630000, 0x10000},
996                         {"default-config", 0x640000, 0x10000},
997                         {"radio", 0x7f0000, 0x10000},
998                         {NULL, 0, 0}
999                 },
1000
1001                 .first_sysupgrade_partition = "os-image",
1002                 .last_sysupgrade_partition = "file-system"
1003         },
1004
1005         /** Firmware layout for the RE355 */
1006         {
1007                 .id = "RE355",
1008                 .vendor = "",
1009                 .support_list =
1010                         "SupportList:\r\n"
1011                         "{product_name:RE355,product_ver:1.0.0,special_id:00000000}\r\n"
1012                         "{product_name:RE355,product_ver:1.0.0,special_id:55530000}\r\n"
1013                         "{product_name:RE355,product_ver:1.0.0,special_id:45550000}\r\n"
1014                         "{product_name:RE355,product_ver:1.0.0,special_id:4A500000}\r\n"
1015                         "{product_name:RE355,product_ver:1.0.0,special_id:43410000}\r\n"
1016                         "{product_name:RE355,product_ver:1.0.0,special_id:41550000}\r\n"
1017                         "{product_name:RE355,product_ver:1.0.0,special_id:4B520000}\r\n"
1018                         "{product_name:RE355,product_ver:1.0.0,special_id:55534100}\r\n",
1019                 .support_trail = '\x00',
1020                 .soft_ver = NULL,
1021
1022                 /**
1023                    The flash partition table for RE355;
1024                    it is almost the same as the one used by the stock images,
1025                    576KB were moved from file-system to os-image.
1026                 */
1027                 .partitions = {
1028                         {"fs-uboot", 0x00000, 0x20000},
1029                         {"os-image", 0x20000, 0x180000},
1030                         {"file-system", 0x1a0000, 0x460000},
1031                         {"partition-table", 0x600000, 0x02000},
1032                         {"default-mac", 0x610000, 0x00020},
1033                         {"pin", 0x610100, 0x00020},
1034                         {"product-info", 0x611100, 0x01000},
1035                         {"soft-version", 0x620000, 0x01000},
1036                         {"support-list", 0x621000, 0x01000},
1037                         {"profile", 0x622000, 0x08000},
1038                         {"user-config", 0x630000, 0x10000},
1039                         {"default-config", 0x640000, 0x10000},
1040                         {"radio", 0x7f0000, 0x10000},
1041                         {NULL, 0, 0}
1042                 },
1043
1044                 .first_sysupgrade_partition = "os-image",
1045                 .last_sysupgrade_partition = "file-system"
1046         },
1047
1048         /** Firmware layout for the RE450 */
1049         {
1050                 .id = "RE450",
1051                 .vendor = "",
1052                 .support_list =
1053                         "SupportList:\r\n"
1054                         "{product_name:RE450,product_ver:1.0.0,special_id:00000000}\r\n"
1055                         "{product_name:RE450,product_ver:1.0.0,special_id:55530000}\r\n"
1056                         "{product_name:RE450,product_ver:1.0.0,special_id:45550000}\r\n"
1057                         "{product_name:RE450,product_ver:1.0.0,special_id:4A500000}\r\n"
1058                         "{product_name:RE450,product_ver:1.0.0,special_id:43410000}\r\n"
1059                         "{product_name:RE450,product_ver:1.0.0,special_id:41550000}\r\n"
1060                         "{product_name:RE450,product_ver:1.0.0,special_id:4B520000}\r\n"
1061                         "{product_name:RE450,product_ver:1.0.0,special_id:55534100}\r\n",
1062                 .support_trail = '\x00',
1063                 .soft_ver = NULL,
1064
1065                 /**
1066                    The flash partition table for RE450;
1067                    it is almost the same as the one used by the stock images,
1068                    576KB were moved from file-system to os-image.
1069                 */
1070                 .partitions = {
1071                         {"fs-uboot", 0x00000, 0x20000},
1072                         {"os-image", 0x20000, 0x180000},
1073                         {"file-system", 0x1a0000, 0x460000},
1074                         {"partition-table", 0x600000, 0x02000},
1075                         {"default-mac", 0x610000, 0x00020},
1076                         {"pin", 0x610100, 0x00020},
1077                         {"product-info", 0x611100, 0x01000},
1078                         {"soft-version", 0x620000, 0x01000},
1079                         {"support-list", 0x621000, 0x01000},
1080                         {"profile", 0x622000, 0x08000},
1081                         {"user-config", 0x630000, 0x10000},
1082                         {"default-config", 0x640000, 0x10000},
1083                         {"radio", 0x7f0000, 0x10000},
1084                         {NULL, 0, 0}
1085                 },
1086
1087                 .first_sysupgrade_partition = "os-image",
1088                 .last_sysupgrade_partition = "file-system"
1089         },
1090
1091         {}
1092 };
1093
1094 #define error(_ret, _errno, _str, ...)                          \
1095         do {                                                    \
1096                 fprintf(stderr, _str ": %s\n", ## __VA_ARGS__,  \
1097                         strerror(_errno));                      \
1098                 if (_ret)                                       \
1099                         exit(_ret);                             \
1100         } while (0)
1101
1102
1103 /** Stores a uint32 as big endian */
1104 static inline void put32(uint8_t *buf, uint32_t val) {
1105         buf[0] = val >> 24;
1106         buf[1] = val >> 16;
1107         buf[2] = val >> 8;
1108         buf[3] = val;
1109 }
1110
1111 /** Allocates a new image partition */
1112 static struct image_partition_entry alloc_image_partition(const char *name, size_t len) {
1113         struct image_partition_entry entry = {name, len, malloc(len)};
1114         if (!entry.data)
1115                 error(1, errno, "malloc");
1116
1117         return entry;
1118 }
1119
1120 /** Frees an image partition */
1121 static void free_image_partition(struct image_partition_entry entry) {
1122         free(entry.data);
1123 }
1124
1125 static time_t source_date_epoch = -1;
1126 static void set_source_date_epoch() {
1127         char *env = getenv("SOURCE_DATE_EPOCH");
1128         char *endptr = env;
1129         errno = 0;
1130         if (env && *env) {
1131                 source_date_epoch = strtoull(env, &endptr, 10);
1132                 if (errno || (endptr && *endptr != '\0')) {
1133                         fprintf(stderr, "Invalid SOURCE_DATE_EPOCH");
1134                         exit(1);
1135                 }
1136         }
1137 }
1138
1139 /** Generates the partition-table partition */
1140 static struct image_partition_entry make_partition_table(const struct flash_partition_entry *p) {
1141         struct image_partition_entry entry = alloc_image_partition("partition-table", 0x800);
1142
1143         char *s = (char *)entry.data, *end = (char *)(s+entry.size);
1144
1145         *(s++) = 0x00;
1146         *(s++) = 0x04;
1147         *(s++) = 0x00;
1148         *(s++) = 0x00;
1149
1150         size_t i;
1151         for (i = 0; p[i].name; i++) {
1152                 size_t len = end-s;
1153                 size_t w = snprintf(s, len, "partition %s base 0x%05x size 0x%05x\n", p[i].name, p[i].base, p[i].size);
1154
1155                 if (w > len-1)
1156                         error(1, 0, "flash partition table overflow?");
1157
1158                 s += w;
1159         }
1160
1161         s++;
1162
1163         memset(s, 0xff, end-s);
1164
1165         return entry;
1166 }
1167
1168
1169 /** Generates a binary-coded decimal representation of an integer in the range [0, 99] */
1170 static inline uint8_t bcd(uint8_t v) {
1171         return 0x10 * (v/10) + v%10;
1172 }
1173
1174
1175 /** Generates the soft-version partition */
1176 static struct image_partition_entry make_soft_version(uint32_t rev) {
1177         struct image_partition_entry entry = alloc_image_partition("soft-version", sizeof(struct soft_version));
1178         struct soft_version *s = (struct soft_version *)entry.data;
1179
1180         time_t t;
1181
1182         if (source_date_epoch != -1)
1183                 t = source_date_epoch;
1184         else if (time(&t) == (time_t)(-1))
1185                 error(1, errno, "time");
1186
1187         struct tm *tm = localtime(&t);
1188
1189         s->magic = htonl(0x0000000c);
1190         s->zero = 0;
1191         s->pad1 = 0xff;
1192
1193         s->version_major = 0;
1194         s->version_minor = 0;
1195         s->version_patch = 0;
1196
1197         s->year_hi = bcd((1900+tm->tm_year)/100);
1198         s->year_lo = bcd(tm->tm_year%100);
1199         s->month = bcd(tm->tm_mon+1);
1200         s->day = bcd(tm->tm_mday);
1201         s->rev = htonl(rev);
1202
1203         s->pad2 = 0xff;
1204
1205         return entry;
1206 }
1207
1208 static struct image_partition_entry make_soft_version_from_string(const char *soft_ver) {
1209         /** String length _including_ the terminating zero byte */
1210         uint32_t ver_len = strlen(soft_ver) + 1;
1211         /** Partition contains 64 bit header, the version string, and one additional null byte */
1212         size_t partition_len = 2*sizeof(uint32_t) + ver_len + 1;
1213         struct image_partition_entry entry = alloc_image_partition("soft-version", partition_len);
1214
1215         uint32_t *len = (uint32_t *)entry.data;
1216         len[0] = htonl(ver_len);
1217         len[1] = 0;
1218         memcpy(&len[2], soft_ver, ver_len);
1219
1220         entry.data[partition_len - 1] = 0;
1221
1222         return entry;
1223 }
1224
1225 /** Generates the support-list partition */
1226 static struct image_partition_entry make_support_list(struct device_info *info) {
1227         size_t len = strlen(info->support_list);
1228         struct image_partition_entry entry = alloc_image_partition("support-list", len + 9);
1229
1230         put32(entry.data, len);
1231         memset(entry.data+4, 0, 4);
1232         memcpy(entry.data+8, info->support_list, len);
1233         entry.data[len+8] = info->support_trail;
1234
1235         return entry;
1236 }
1237
1238 /** Creates a new image partition with an arbitrary name from a file */
1239 static struct image_partition_entry read_file(const char *part_name, const char *filename, bool add_jffs2_eof, struct flash_partition_entry *file_system_partition) {
1240         struct stat statbuf;
1241
1242         if (stat(filename, &statbuf) < 0)
1243                 error(1, errno, "unable to stat file `%s'", filename);
1244
1245         size_t len = statbuf.st_size;
1246
1247         if (add_jffs2_eof)
1248                 if (file_system_partition)
1249                         len = ALIGN(len + file_system_partition->base, 0x10000) + sizeof(jffs2_eof_mark) - file_system_partition->base;
1250                 else
1251                         len = ALIGN(len, 0x10000) + sizeof(jffs2_eof_mark);
1252
1253         struct image_partition_entry entry = alloc_image_partition(part_name, len);
1254
1255         FILE *file = fopen(filename, "rb");
1256         if (!file)
1257                 error(1, errno, "unable to open file `%s'", filename);
1258
1259         if (fread(entry.data, statbuf.st_size, 1, file) != 1)
1260                 error(1, errno, "unable to read file `%s'", filename);
1261
1262         if (add_jffs2_eof) {
1263                 uint8_t *eof = entry.data + statbuf.st_size, *end = entry.data+entry.size;
1264
1265                 memset(eof, 0xff, end - eof - sizeof(jffs2_eof_mark));
1266                 memcpy(end - sizeof(jffs2_eof_mark), jffs2_eof_mark, sizeof(jffs2_eof_mark));
1267         }
1268
1269         fclose(file);
1270
1271         return entry;
1272 }
1273
1274 /** Creates a new image partition from arbitrary data */
1275 static struct image_partition_entry put_data(const char *part_name, const char *datain, size_t len) {
1276
1277         struct image_partition_entry entry = alloc_image_partition(part_name, len);
1278
1279         memcpy(entry.data, datain, len);
1280
1281         return entry;
1282 }
1283
1284 /**
1285    Copies a list of image partitions into an image buffer and generates the image partition table while doing so
1286
1287    Example image partition table:
1288
1289      fwup-ptn partition-table base 0x00800 size 0x00800
1290      fwup-ptn os-image base 0x01000 size 0x113b45
1291      fwup-ptn file-system base 0x114b45 size 0x1d0004
1292      fwup-ptn support-list base 0x2e4b49 size 0x000d1
1293
1294    Each line of the partition table is terminated with the bytes 09 0d 0a ("\t\r\n"),
1295    the end of the partition table is marked with a zero byte.
1296
1297    The firmware image must contain at least the partition-table and support-list partitions
1298    to be accepted. There aren't any alignment constraints for the image partitions.
1299
1300    The partition-table partition contains the actual flash layout; partitions
1301    from the image partition table are mapped to the corresponding flash partitions during
1302    the firmware upgrade. The support-list partition contains a list of devices supported by
1303    the firmware image.
1304
1305    The base offsets in the firmware partition table are relative to the end
1306    of the vendor information block, so the partition-table partition will
1307    actually start at offset 0x1814 of the image.
1308
1309    I think partition-table must be the first partition in the firmware image.
1310 */
1311 static void put_partitions(uint8_t *buffer, const struct flash_partition_entry *flash_parts, const struct image_partition_entry *parts) {
1312         size_t i, j;
1313         char *image_pt = (char *)buffer, *end = image_pt + 0x800;
1314
1315         size_t base = 0x800;
1316         for (i = 0; parts[i].name; i++) {
1317                 for (j = 0; flash_parts[j].name; j++) {
1318                         if (!strcmp(flash_parts[j].name, parts[i].name)) {
1319                                 if (parts[i].size > flash_parts[j].size)
1320                                         error(1, 0, "%s partition too big (more than %u bytes)", flash_parts[j].name, (unsigned)flash_parts[j].size);
1321                                 break;
1322                         }
1323                 }
1324
1325                 assert(flash_parts[j].name);
1326
1327                 memcpy(buffer + base, parts[i].data, parts[i].size);
1328
1329                 size_t len = end-image_pt;
1330                 size_t w = snprintf(image_pt, len, "fwup-ptn %s base 0x%05x size 0x%05x\t\r\n", parts[i].name, (unsigned)base, (unsigned)parts[i].size);
1331
1332                 if (w > len-1)
1333                         error(1, 0, "image partition table overflow?");
1334
1335                 image_pt += w;
1336
1337                 base += parts[i].size;
1338         }
1339 }
1340
1341 /** Generates and writes the image MD5 checksum */
1342 static void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) {
1343         MD5_CTX ctx;
1344
1345         MD5_Init(&ctx);
1346         MD5_Update(&ctx, md5_salt, (unsigned int)sizeof(md5_salt));
1347         MD5_Update(&ctx, buffer, len);
1348         MD5_Final(md5, &ctx);
1349 }
1350
1351
1352 /**
1353    Generates the firmware image in factory format
1354
1355    Image format:
1356
1357      Bytes (hex)  Usage
1358      -----------  -----
1359      0000-0003    Image size (4 bytes, big endian)
1360      0004-0013    MD5 hash (hash of a 16 byte salt and the image data starting with byte 0x14)
1361      0014-0017    Vendor information length (without padding) (4 bytes, big endian)
1362      0018-1013    Vendor information (4092 bytes, padded with 0xff; there seem to be older
1363                   (VxWorks-based) TP-LINK devices which use a smaller vendor information block)
1364      1014-1813    Image partition table (2048 bytes, padded with 0xff)
1365      1814-xxxx    Firmware partitions
1366 */
1367 static void * generate_factory_image(struct device_info *info, const struct image_partition_entry *parts, size_t *len) {
1368         *len = 0x1814;
1369
1370         size_t i;
1371         for (i = 0; parts[i].name; i++)
1372                 *len += parts[i].size;
1373
1374         uint8_t *image = malloc(*len);
1375         if (!image)
1376                 error(1, errno, "malloc");
1377
1378         memset(image, 0xff, *len);
1379         put32(image, *len);
1380
1381         if (info->vendor) {
1382                 size_t vendor_len = strlen(info->vendor);
1383                 put32(image+0x14, vendor_len);
1384                 memcpy(image+0x18, info->vendor, vendor_len);
1385         }
1386
1387         put_partitions(image + 0x1014, info->partitions, parts);
1388         put_md5(image+0x04, image+0x14, *len-0x14);
1389
1390         return image;
1391 }
1392
1393 /**
1394    Generates the firmware image in sysupgrade format
1395
1396    This makes some assumptions about the provided flash and image partition tables and
1397    should be generalized when TP-LINK starts building its safeloader into hardware with
1398    different flash layouts.
1399 */
1400 static void * generate_sysupgrade_image(struct device_info *info, const struct image_partition_entry *image_parts, size_t *len) {
1401         size_t i, j;
1402         size_t flash_first_partition_index = 0;
1403         size_t flash_last_partition_index = 0;
1404         const struct flash_partition_entry *flash_first_partition = NULL;
1405         const struct flash_partition_entry *flash_last_partition = NULL;
1406         const struct image_partition_entry *image_last_partition = NULL;
1407
1408         /** Find first and last partitions */
1409         for (i = 0; info->partitions[i].name; i++) {
1410                 if (!strcmp(info->partitions[i].name, info->first_sysupgrade_partition)) {
1411                         flash_first_partition = &info->partitions[i];
1412                         flash_first_partition_index = i;
1413                 } else if (!strcmp(info->partitions[i].name, info->last_sysupgrade_partition)) {
1414                         flash_last_partition = &info->partitions[i];
1415                         flash_last_partition_index = i;
1416                 }
1417         }
1418
1419         assert(flash_first_partition && flash_last_partition);
1420         assert(flash_first_partition_index < flash_last_partition_index);
1421
1422         /** Find last partition from image to calculate needed size */
1423         for (i = 0; image_parts[i].name; i++) {
1424                 if (!strcmp(image_parts[i].name, info->last_sysupgrade_partition)) {
1425                         image_last_partition = &image_parts[i];
1426                         break;
1427                 }
1428         }
1429
1430         assert(image_last_partition);
1431
1432         *len = flash_last_partition->base - flash_first_partition->base + image_last_partition->size;
1433
1434         uint8_t *image = malloc(*len);
1435         if (!image)
1436                 error(1, errno, "malloc");
1437
1438         memset(image, 0xff, *len);
1439
1440         for (i = flash_first_partition_index; i <= flash_last_partition_index; i++) {
1441                 for (j = 0; image_parts[j].name; j++) {
1442                         if (!strcmp(info->partitions[i].name, image_parts[j].name)) {
1443                                 if (image_parts[j].size > info->partitions[i].size)
1444                                         error(1, 0, "%s partition too big (more than %u bytes)", info->partitions[i].name, (unsigned)info->partitions[i].size);
1445                                 memcpy(image + info->partitions[i].base - flash_first_partition->base, image_parts[j].data, image_parts[j].size);
1446                                 break;
1447                         }
1448
1449                         assert(image_parts[j].name);
1450                 }
1451         }
1452
1453         return image;
1454 }
1455
1456 /** Generates an image according to a given layout and writes it to a file */
1457 static void build_image(const char *output,
1458                 const char *kernel_image,
1459                 const char *rootfs_image,
1460                 uint32_t rev,
1461                 bool add_jffs2_eof,
1462                 bool sysupgrade,
1463                 struct device_info *info) {
1464
1465         size_t i;
1466
1467         struct image_partition_entry parts[7] = {};
1468
1469         struct flash_partition_entry *firmware_partition = NULL;
1470         struct flash_partition_entry *os_image_partition = NULL;
1471         struct flash_partition_entry *file_system_partition = NULL;
1472         size_t firmware_partition_index = 0;
1473
1474         for (i = 0; info->partitions[i].name; i++) {
1475                 if (!strcmp(info->partitions[i].name, "firmware"))
1476                 {
1477                         firmware_partition = &info->partitions[i];
1478                         firmware_partition_index = i;
1479                 }
1480         }
1481
1482         if (firmware_partition)
1483         {
1484                 os_image_partition = &info->partitions[firmware_partition_index];
1485                 file_system_partition = &info->partitions[firmware_partition_index + 1];
1486
1487                 struct stat kernel;
1488                 if (stat(kernel_image, &kernel) < 0)
1489                         error(1, errno, "unable to stat file `%s'", kernel_image);
1490
1491                 if (kernel.st_size > firmware_partition->size)
1492                         error(1, 0, "kernel overflowed firmware partition\n");
1493
1494                 for (i = MAX_PARTITIONS-1; i >= firmware_partition_index + 1; i--)
1495                         info->partitions[i+1] = info->partitions[i];
1496
1497                 file_system_partition->name = "file-system";
1498                 file_system_partition->base = firmware_partition->base + kernel.st_size;
1499
1500                 /* Align partition start to erase blocks for factory images only */
1501                 if (!sysupgrade)
1502                         file_system_partition->base = ALIGN(firmware_partition->base + kernel.st_size, 0x10000);
1503
1504                 file_system_partition->size = firmware_partition->size - file_system_partition->base;
1505
1506                 os_image_partition->name = "os-image";
1507                 os_image_partition->size = kernel.st_size;
1508         }
1509
1510         parts[0] = make_partition_table(info->partitions);
1511         if (info->soft_ver)
1512                 parts[1] = make_soft_version_from_string(info->soft_ver);
1513         else
1514                 parts[1] = make_soft_version(rev);
1515
1516         parts[2] = make_support_list(info);
1517         parts[3] = read_file("os-image", kernel_image, false, NULL);
1518         parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof, file_system_partition);
1519
1520         /* Some devices need the extra-para partition to accept the firmware */
1521         if (strcasecmp(info->id, "ARCHER-C25-V1") == 0 ||
1522             strcasecmp(info->id, "ARCHER-C60-V2") == 0 ||
1523             strcasecmp(info->id, "TLWR1043NV5") == 0) {
1524                 const char mdat[11] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00};
1525                 parts[5] = put_data("extra-para", mdat, 11);
1526         } else if (strcasecmp(info->id, "ARCHER-C7-V4") == 0 || strcasecmp(info->id, "ARCHER-C7-V5") == 0) {
1527                 const char mdat[11] = {0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0xca, 0x00, 0x01, 0x00, 0x00};
1528                 parts[5] = put_data("extra-para", mdat, 11);
1529         }
1530
1531         size_t len;
1532         void *image;
1533         if (sysupgrade)
1534                 image = generate_sysupgrade_image(info, parts, &len);
1535         else
1536                 image = generate_factory_image(info, parts, &len);
1537
1538         FILE *file = fopen(output, "wb");
1539         if (!file)
1540                 error(1, errno, "unable to open output file");
1541
1542         if (fwrite(image, len, 1, file) != 1)
1543                 error(1, 0, "unable to write output file");
1544
1545         fclose(file);
1546
1547         free(image);
1548
1549         for (i = 0; parts[i].name; i++)
1550                 free_image_partition(parts[i]);
1551 }
1552
1553 /** Usage output */
1554 static void usage(const char *argv0) {
1555         fprintf(stderr,
1556                 "Usage: %s [OPTIONS...]\n"
1557                 "\n"
1558                 "Options:\n"
1559                 "  -h              show this help\n"
1560                 "\n"
1561                 "Create a new image:\n"
1562                 "  -B <board>      create image for the board specified with <board>\n"
1563                 "  -k <file>       read kernel image from the file <file>\n"
1564                 "  -r <file>       read rootfs image from the file <file>\n"
1565                 "  -o <file>       write output to the file <file>\n"
1566                 "  -V <rev>        sets the revision number to <rev>\n"
1567                 "  -j              add jffs2 end-of-filesystem markers\n"
1568                 "  -S              create sysupgrade instead of factory image\n"
1569                 "Extract an old image:\n"
1570                 "  -x <file>       extract all oem firmware partition\n"
1571                 "  -d <dir>        destination to extract the firmware partition\n"
1572                 "  -z <file>       convert an oem firmware into a sysupgade file. Use -o for output file\n",
1573                 argv0
1574         );
1575 };
1576
1577
1578 static struct device_info *find_board(const char *id)
1579 {
1580         struct device_info *board = NULL;
1581
1582         for (board = boards; board->id != NULL; board++)
1583                 if (strcasecmp(id, board->id) == 0)
1584                         return board;
1585
1586         return NULL;
1587 }
1588
1589 static int add_flash_partition(
1590                 struct flash_partition_entry *part_list,
1591                 size_t max_entries,
1592                 const char *name,
1593                 unsigned long base,
1594                 unsigned long size)
1595 {
1596         int ptr;
1597         /* check if the list has a free entry */
1598         for (ptr = 0; ptr < max_entries; ptr++, part_list++) {
1599                 if (part_list->name == NULL &&
1600                                 part_list->base == 0 &&
1601                                 part_list->size == 0)
1602                         break;
1603         }
1604
1605         if (ptr == max_entries) {
1606                 error(1, 0, "No free flash part entry available.");
1607         }
1608
1609         part_list->name = calloc(1, strlen(name) + 1);
1610         memcpy((char *)part_list->name, name, strlen(name));
1611         part_list->base = base;
1612         part_list->size = size;
1613
1614         return 0;
1615 }
1616
1617 /** read the partition table into struct flash_partition_entry */
1618 static int read_partition_table(
1619                 FILE *file, long offset,
1620                 struct flash_partition_entry *entries, size_t max_entries,
1621                 int type)
1622 {
1623         char buf[2048];
1624         char *ptr, *end;
1625         const char *parthdr = NULL;
1626         const char *fwuphdr = "fwup-ptn";
1627         const char *flashhdr = "partition";
1628
1629         /* TODO: search for the partition table */
1630
1631         switch(type) {
1632                 case 0:
1633                         parthdr = fwuphdr;
1634                         break;
1635                 case 1:
1636                         parthdr = flashhdr;
1637                         break;
1638                 default:
1639                         error(1, 0, "Invalid partition table");
1640         }
1641
1642         if (fseek(file, offset, SEEK_SET) < 0)
1643                 error(1, errno, "Can not seek in the firmware");
1644
1645         if (fread(buf, 1, 2048, file) < 0)
1646                 error(1, errno, "Can not read fwup-ptn from the firmware");
1647
1648         buf[2047] = '\0';
1649
1650         /* look for the partition header */
1651         if (memcmp(buf, parthdr, strlen(parthdr)) != 0) {
1652                 fprintf(stderr, "DEBUG: can not find fwuphdr\n");
1653                 return 1;
1654         }
1655
1656         ptr = buf;
1657         end = buf + sizeof(buf);
1658         while ((ptr + strlen(parthdr)) < end &&
1659                         memcmp(ptr, parthdr, strlen(parthdr)) == 0) {
1660                 char *end_part;
1661                 char *end_element;
1662
1663                 char name[32] = { 0 };
1664                 int name_len = 0;
1665                 unsigned long base = 0;
1666                 unsigned long size = 0;
1667
1668                 end_part = memchr(ptr, '\n', (end - ptr));
1669                 if (end_part == NULL) {
1670                         /* in theory this should never happen, because a partition always ends with 0x09, 0x0D, 0x0A */
1671                         break;
1672                 }
1673
1674                 for (int i = 0; i <= 4; i++) {
1675                         if (end_part <= ptr)
1676                                 break;
1677
1678                         end_element = memchr(ptr, 0x20, (end_part - ptr));
1679                         if (end_element == NULL) {
1680                                 error(1, errno, "Ignoring the rest of the partition entries.");
1681                                 break;
1682                         }
1683
1684                         switch (i) {
1685                                 /* partition header */
1686                                 case 0:
1687                                         ptr = end_element + 1;
1688                                         continue;
1689                                 /* name */
1690                                 case 1:
1691                                         name_len = (end_element - ptr) > 31 ? 31 : (end_element - ptr);
1692                                         strncpy(name, ptr, name_len);
1693                                         name[name_len] = '\0';
1694                                         ptr = end_element + 1;
1695                                         continue;
1696
1697                                 /* string "base" */
1698                                 case 2:
1699                                         ptr = end_element + 1;
1700                                         continue;
1701
1702                                 /* actual base */
1703                                 case 3:
1704                                         base = strtoul(ptr, NULL, 16);
1705                                         ptr = end_element + 1;
1706                                         continue;
1707
1708                                 /* string "size" */
1709                                 case 4:
1710                                         ptr = end_element + 1;
1711                                         /* actual size. The last element doesn't have a sepeartor */
1712                                         size = strtoul(ptr, NULL, 16);
1713                                         /* the part ends with 0x09, 0x0d, 0x0a */
1714                                         ptr = end_part + 1;
1715                                         add_flash_partition(entries, max_entries, name, base, size);
1716                                         continue;
1717                         }
1718                 }
1719         }
1720
1721         return 0;
1722 }
1723
1724 static void write_partition(
1725                 FILE *input_file,
1726                 size_t firmware_offset,
1727                 struct flash_partition_entry *entry,
1728                 FILE *output_file)
1729 {
1730         char buf[4096];
1731         size_t offset;
1732
1733         fseek(input_file, entry->base + firmware_offset, SEEK_SET);
1734
1735         for (offset = 0; sizeof(buf) + offset <= entry->size; offset += sizeof(buf)) {
1736                 if (fread(buf, sizeof(buf), 1, input_file) < 0)
1737                         error(1, errno, "Can not read partition from input_file");
1738
1739                 if (fwrite(buf, sizeof(buf), 1, output_file) < 0)
1740                         error(1, errno, "Can not write partition to output_file");
1741         }
1742         /* write last chunk smaller than buffer */
1743         if (offset < entry->size) {
1744                 offset = entry->size - offset;
1745                 if (fread(buf, offset, 1, input_file) < 0)
1746                         error(1, errno, "Can not read partition from input_file");
1747                 if (fwrite(buf, offset, 1, output_file) < 0)
1748                         error(1, errno, "Can not write partition to output_file");
1749         }
1750 }
1751
1752 static int extract_firmware_partition(FILE *input_file, size_t firmware_offset, struct flash_partition_entry *entry, const char *output_directory)
1753 {
1754         FILE *output_file;
1755         char output[PATH_MAX];
1756
1757         snprintf(output, PATH_MAX, "%s/%s", output_directory, entry->name);
1758         output_file = fopen(output, "wb+");
1759         if (output_file == NULL) {
1760                 error(1, errno, "Can not open output file %s", output);
1761         }
1762
1763         write_partition(input_file, firmware_offset, entry, output_file);
1764
1765         fclose(output_file);
1766
1767         return 0;
1768 }
1769
1770 /** extract all partitions from the firmware file */
1771 static int extract_firmware(const char *input, const char *output_directory)
1772 {
1773         struct flash_partition_entry entries[16] = { 0 };
1774         size_t max_entries = 16;
1775         size_t firmware_offset = 0x1014;
1776         FILE *input_file;
1777
1778         struct stat statbuf;
1779
1780         /* check input file */
1781         if (stat(input, &statbuf)) {
1782                 error(1, errno, "Can not read input firmware %s", input);
1783         }
1784
1785         /* check if output directory exists */
1786         if (stat(output_directory, &statbuf)) {
1787                 error(1, errno, "Failed to stat output directory %s", output_directory);
1788         }
1789
1790         if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
1791                 error(1, errno, "Given output directory is not a directory %s", output_directory);
1792         }
1793
1794         input_file = fopen(input, "rb");
1795
1796         if (read_partition_table(input_file, firmware_offset, entries, 16, 0) != 0) {
1797                 error(1, 0, "Error can not read the partition table (fwup-ptn)");
1798         }
1799
1800         for (int i = 0; i < max_entries; i++) {
1801                 if (entries[i].name == NULL &&
1802                                 entries[i].base == 0 &&
1803                                 entries[i].size == 0)
1804                         continue;
1805
1806                 extract_firmware_partition(input_file, firmware_offset, &entries[i], output_directory);
1807         }
1808
1809         return 0;
1810 }
1811
1812 static struct flash_partition_entry *find_partition(
1813                 struct flash_partition_entry *entries, size_t max_entries,
1814                 const char *name, const char *error_msg)
1815 {
1816         for (int i = 0; i < max_entries; i++, entries++) {
1817                 if (strcmp(entries->name, name) == 0)
1818                         return entries;
1819         }
1820
1821         error(1, 0, "%s", error_msg);
1822         return NULL;
1823 }
1824
1825 static void write_ff(FILE *output_file, size_t size)
1826 {
1827         char buf[4096];
1828         int offset;
1829
1830         memset(buf, 0xff, sizeof(buf));
1831
1832         for (offset = 0; offset + sizeof(buf) < size ; offset += sizeof(buf)) {
1833                 if (fwrite(buf, sizeof(buf), 1, output_file) < 0)
1834                         error(1, errno, "Can not write 0xff to output_file");
1835         }
1836
1837         /* write last chunk smaller than buffer */
1838         if (offset < size) {
1839                 offset = size - offset;
1840                 if (fwrite(buf, offset, 1, output_file) < 0)
1841                         error(1, errno, "Can not write partition to output_file");
1842         }
1843 }
1844
1845 static void convert_firmware(const char *input, const char *output)
1846 {
1847         struct flash_partition_entry fwup[MAX_PARTITIONS] = { 0 };
1848         struct flash_partition_entry flash[MAX_PARTITIONS] = { 0 };
1849         struct flash_partition_entry *fwup_os_image = NULL, *fwup_file_system = NULL;
1850         struct flash_partition_entry *flash_os_image = NULL, *flash_file_system = NULL;
1851         struct flash_partition_entry *fwup_partition_table = NULL;
1852         size_t firmware_offset = 0x1014;
1853         FILE *input_file, *output_file;
1854
1855         struct stat statbuf;
1856
1857         /* check input file */
1858         if (stat(input, &statbuf)) {
1859                 error(1, errno, "Can not read input firmware %s", input);
1860         }
1861
1862         input_file = fopen(input, "rb");
1863         if (!input_file)
1864                 error(1, 0, "Can not open input firmware %s", input);
1865
1866         output_file = fopen(output, "wb");
1867         if (!output_file)
1868                 error(1, 0, "Can not open output firmware %s", output);
1869
1870         if (read_partition_table(input_file, firmware_offset, fwup, MAX_PARTITIONS, 0) != 0) {
1871                 error(1, 0, "Error can not read the partition table (fwup-ptn)");
1872         }
1873
1874         fwup_os_image = find_partition(fwup, MAX_PARTITIONS,
1875                         "os-image", "Error can not find os-image partition (fwup)");
1876         fwup_file_system = find_partition(fwup, MAX_PARTITIONS,
1877                         "file-system", "Error can not find file-system partition (fwup)");
1878         fwup_partition_table = find_partition(fwup, MAX_PARTITIONS,
1879                         "partition-table", "Error can not find partition-table partition");
1880
1881         /* the flash partition table has a 0x00000004 magic haeder */
1882         if (read_partition_table(input_file, firmware_offset + fwup_partition_table->base + 4, flash, MAX_PARTITIONS, 1) != 0)
1883                 error(1, 0, "Error can not read the partition table (flash)");
1884
1885         flash_os_image = find_partition(flash, MAX_PARTITIONS,
1886                         "os-image", "Error can not find os-image partition (flash)");
1887         flash_file_system = find_partition(flash, MAX_PARTITIONS,
1888                         "file-system", "Error can not find file-system partition (flash)");
1889
1890         /* write os_image to 0x0 */
1891         write_partition(input_file, firmware_offset, fwup_os_image, output_file);
1892         write_ff(output_file, flash_os_image->size - fwup_os_image->size);
1893
1894         /* write file-system behind os_image */
1895         fseek(output_file, flash_file_system->base - flash_os_image->base, SEEK_SET);
1896         write_partition(input_file, firmware_offset, fwup_file_system, output_file);
1897         write_ff(output_file, flash_file_system->size - fwup_file_system->size);
1898
1899         fclose(output_file);
1900         fclose(input_file);
1901 }
1902
1903 int main(int argc, char *argv[]) {
1904         const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL;
1905         const char *extract_image = NULL, *output_directory = NULL, *convert_image = NULL;
1906         bool add_jffs2_eof = false, sysupgrade = false;
1907         unsigned rev = 0;
1908         struct device_info *info;
1909         set_source_date_epoch();
1910
1911         while (true) {
1912                 int c;
1913
1914                 c = getopt(argc, argv, "B:k:r:o:V:jSh:x:d:z:");
1915                 if (c == -1)
1916                         break;
1917
1918                 switch (c) {
1919                 case 'B':
1920                         board = optarg;
1921                         break;
1922
1923                 case 'k':
1924                         kernel_image = optarg;
1925                         break;
1926
1927                 case 'r':
1928                         rootfs_image = optarg;
1929                         break;
1930
1931                 case 'o':
1932                         output = optarg;
1933                         break;
1934
1935                 case 'V':
1936                         sscanf(optarg, "r%u", &rev);
1937                         break;
1938
1939                 case 'j':
1940                         add_jffs2_eof = true;
1941                         break;
1942
1943                 case 'S':
1944                         sysupgrade = true;
1945                         break;
1946
1947                 case 'h':
1948                         usage(argv[0]);
1949                         return 0;
1950
1951                 case 'd':
1952                         output_directory = optarg;
1953                         break;
1954
1955                 case 'x':
1956                         extract_image = optarg;
1957                         break;
1958
1959                 case 'z':
1960                         convert_image = optarg;
1961                         break;
1962
1963                 default:
1964                         usage(argv[0]);
1965                         return 1;
1966                 }
1967         }
1968
1969         if (extract_image || output_directory) {
1970                 if (!extract_image)
1971                         error(1, 0, "No factory/oem image given via -x <file>. Output directory is only valid with -x");
1972                 if (!output_directory)
1973                         error(1, 0, "Can not extract an image without output directory. Use -d <dir>");
1974                 extract_firmware(extract_image, output_directory);
1975         } else if (convert_image) {
1976                 if (!output)
1977                         error(1, 0, "Can not convert a factory/oem image into sysupgrade image without output file. Use -o <file>");
1978                 convert_firmware(convert_image, output);
1979         } else {
1980                 if (!board)
1981                         error(1, 0, "no board has been specified");
1982                 if (!kernel_image)
1983                         error(1, 0, "no kernel image has been specified");
1984                 if (!rootfs_image)
1985                         error(1, 0, "no rootfs image has been specified");
1986                 if (!output)
1987                         error(1, 0, "no output filename has been specified");
1988
1989                 info = find_board(board);
1990
1991                 if (info == NULL)
1992                         error(1, 0, "unsupported board %s", board);
1993
1994                 build_image(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade, info);
1995         }
1996
1997         return 0;
1998 }