firmware-utils: tplink-safeloader: add Archer C9 support
[librecmc/librecmc.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
50 #include "md5.h"
51
52
53 #define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); })
54
55
56 /** An image partition table entry */
57 struct image_partition_entry {
58         const char *name;
59         size_t size;
60         uint8_t *data;
61 };
62
63 /** A flash partition table entry */
64 struct flash_partition_entry {
65         const char *name;
66         uint32_t base;
67         uint32_t size;
68 };
69
70 struct device_info {
71         const char *vendor;
72         const char *support_list;
73         const struct flash_partition_entry *partitions;
74         void *(*generate_sysupgrade_image)(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len);
75 };
76
77 /** The content of the soft-version structure */
78 struct __attribute__((__packed__)) soft_version {
79         uint32_t magic;
80         uint32_t zero;
81         uint8_t pad1;
82         uint8_t version_major;
83         uint8_t version_minor;
84         uint8_t version_patch;
85         uint8_t year_hi;
86         uint8_t year_lo;
87         uint8_t month;
88         uint8_t day;
89         uint32_t rev;
90         uint8_t pad2;
91 };
92
93
94 static const uint8_t jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde};
95
96
97 /**
98    Salt for the MD5 hash
99
100    Fortunately, TP-LINK seems to use the same salt for most devices which use
101    the new image format.
102 */
103 static const uint8_t md5_salt[16] = {
104         0x7a, 0x2b, 0x15, 0xed,
105         0x9b, 0x98, 0x59, 0x6d,
106         0xe5, 0x04, 0xab, 0x44,
107         0xac, 0x2a, 0x9f, 0x4e,
108 };
109
110
111 /** Vendor information for CPE210/220/510/520 */
112 static const char cpe510_vendor[] = "CPE510(TP-LINK|UN|N300-5):1.0\r\n";
113
114 /** Vendor information for C2600 */
115 static const char c2600_vendor[] = "";
116
117 /** Vendor information for EAP120 */
118 static const char eap120_vendor[] = "EAP120(TP-LINK|UN|N300-2):1.0\r\n";
119
120 /**
121     The flash partition table for CPE210/220/510/520;
122     it is the same as the one used by the stock images.
123 */
124 static const struct flash_partition_entry cpe510_partitions[] = {
125         {"fs-uboot", 0x00000, 0x20000},
126         {"partition-table", 0x20000, 0x02000},
127         {"default-mac", 0x30000, 0x00020},
128         {"product-info", 0x31100, 0x00100},
129         {"signature", 0x32000, 0x00400},
130         {"os-image", 0x40000, 0x170000},
131         {"soft-version", 0x1b0000, 0x00100},
132         {"support-list", 0x1b1000, 0x00400},
133         {"file-system", 0x1c0000, 0x600000},
134         {"user-config", 0x7c0000, 0x10000},
135         {"default-config", 0x7d0000, 0x10000},
136         {"log", 0x7e0000, 0x10000},
137         {"radio", 0x7f0000, 0x10000},
138         {NULL, 0, 0}
139 };
140
141 /**
142     The flash partition table for C2600;
143     it is the same as the one used by the stock images.
144 */
145 static const struct flash_partition_entry c2600_partitions[] = {
146         {"SBL1", 0x00000, 0x20000},
147         {"MIBIB", 0x20000, 0x20000},
148         {"SBL2", 0x40000, 0x20000},
149         {"SBL3", 0x60000, 0x30000},
150         {"DDRCONFIG", 0x90000, 0x10000},
151         {"SSD", 0xa0000, 0x10000},
152         {"TZ", 0xb0000, 0x30000},
153         {"RPM", 0xe0000, 0x20000},
154         {"fs-uboot", 0x100000, 0x70000},
155         {"uboot-env", 0x170000, 0x40000},
156         {"radio", 0x1b0000, 0x40000},
157         {"os-image", 0x1f0000, 0x200000},
158         {"file-system", 0x3f0000, 0x1b00000},
159         {"default-mac", 0x1ef0000, 0x00200},
160         {"pin", 0x1ef0200, 0x00200},
161         {"product-info", 0x1ef0400, 0x0fc00},
162         {"partition-table", 0x1f00000, 0x10000},
163         {"soft-version", 0x1f10000, 0x10000},
164         {"support-list", 0x1f20000, 0x10000},
165         {"profile", 0x1f30000, 0x10000},
166         {"default-config", 0x1f40000, 0x10000},
167         {"user-config", 0x1f50000, 0x40000},
168         {"qos-db", 0x1f90000, 0x40000},
169         {"usb-config", 0x1fd0000, 0x10000},
170         {"log", 0x1fe0000, 0x20000},
171         {NULL, 0, 0}
172 };
173
174 static const struct flash_partition_entry c5_partitions[] = {
175         {"fs-uboot", 0x00000, 0x40000},
176         {"os-image", 0x40000, 0x200000},
177         {"file-system", 0x240000, 0xc00000},
178         {"default-mac", 0xe40000, 0x00200},
179         {"pin", 0xe40200, 0x00200},
180         {"product-info", 0xe40400, 0x00200},
181         {"partition-table", 0xe50000, 0x10000},
182         {"soft-version", 0xe60000, 0x00200},
183         {"support-list", 0xe61000, 0x0f000},
184         {"profile", 0xe70000, 0x10000},
185         {"default-config", 0xe80000, 0x10000},
186         {"user-config", 0xe90000, 0x50000},
187         {"log", 0xee0000, 0x100000},
188         {"radio_bk", 0xfe0000, 0x10000},
189         {"radio", 0xff0000, 0x10000},
190         {NULL, 0, 0}
191 };
192
193 /**    The flash partition table for EAP120;
194     it is the same as the one used by the stock images.
195 */
196 static const struct flash_partition_entry eap120_partitions[] = {
197         {"fs-uboot", 0x00000, 0x20000},
198         {"partition-table", 0x20000, 0x02000},
199         {"default-mac", 0x30000, 0x00020},
200         {"support-list", 0x31000, 0x00100},
201         {"product-info", 0x31100, 0x00100},
202         {"soft-version", 0x32000, 0x00100},
203         {"os-image", 0x40000, 0x180000},
204         {"file-system", 0x1c0000, 0x600000},
205         {"user-config", 0x7c0000, 0x10000},
206         {"backup-config", 0x7d0000, 0x10000},
207         {"log", 0x7e0000, 0x10000},
208         {"radio", 0x7f0000, 0x10000},
209         {NULL, 0, 0}
210 };
211
212 /**
213    The support list for CPE210/220
214 */
215 static const char cpe210_support_list[] =
216         "SupportList:\r\n"
217         "CPE210(TP-LINK|UN|N300-2):1.0\r\n"
218         "CPE210(TP-LINK|UN|N300-2):1.1\r\n"
219         "CPE220(TP-LINK|UN|N300-2):1.0\r\n"
220         "CPE220(TP-LINK|UN|N300-2):1.1\r\n";
221 /**
222    The support list for CPE210/220/510/520
223 */
224 static const char cpe510_support_list[] =
225         "SupportList:\r\n"
226         "CPE510(TP-LINK|UN|N300-5):1.0\r\n"
227         "CPE510(TP-LINK|UN|N300-5):1.1\r\n"
228         "CPE520(TP-LINK|UN|N300-5):1.0\r\n"
229         "CPE520(TP-LINK|UN|N300-5):1.1\r\n";
230
231 /**
232    The support list for C2600
233 */
234 static const char c2600_support_list[] =
235         "SupportList:\r\n"
236         "{product_name:Archer C2600,product_ver:1.0.0,special_id:00000000}\r\n";
237
238 static const char c9_support_list[] =
239         "SupportList:\n"
240         "{product_name:ArcherC9,"
241         "product_ver:1.0.0,"
242         "special_id:00000000}\n";
243
244 /**
245    The support list for EAP120
246 */
247 static const char eap120_support_list[] =
248         "SupportList:\r\n"
249         "EAP120(TP-LINK|UN|N300-2):1.0\r\n";
250
251 #define error(_ret, _errno, _str, ...)                          \
252         do {                                                    \
253                 fprintf(stderr, _str ": %s\n", ## __VA_ARGS__,  \
254                         strerror(_errno));                      \
255                 if (_ret)                                       \
256                         exit(_ret);                             \
257         } while (0)
258
259
260 /** Stores a uint32 as big endian */
261 static inline void put32(uint8_t *buf, uint32_t val) {
262         buf[0] = val >> 24;
263         buf[1] = val >> 16;
264         buf[2] = val >> 8;
265         buf[3] = val;
266 }
267
268 /** Allocates a new image partition */
269 static struct image_partition_entry alloc_image_partition(const char *name, size_t len) {
270         struct image_partition_entry entry = {name, len, malloc(len)};
271         if (!entry.data)
272                 error(1, errno, "malloc");
273
274         return entry;
275 }
276
277 /** Frees an image partition */
278 static void free_image_partition(struct image_partition_entry entry) {
279         free(entry.data);
280 }
281
282 /** Generates the partition-table partition */
283 static struct image_partition_entry make_partition_table(const struct flash_partition_entry *p) {
284         struct image_partition_entry entry = alloc_image_partition("partition-table", 0x800);
285
286         char *s = (char *)entry.data, *end = (char *)(s+entry.size);
287
288         *(s++) = 0x00;
289         *(s++) = 0x04;
290         *(s++) = 0x00;
291         *(s++) = 0x00;
292
293         size_t i;
294         for (i = 0; p[i].name; i++) {
295                 size_t len = end-s;
296                 size_t w = snprintf(s, len, "partition %s base 0x%05x size 0x%05x\n", p[i].name, p[i].base, p[i].size);
297
298                 if (w > len-1)
299                         error(1, 0, "flash partition table overflow?");
300
301                 s += w;
302         }
303
304         s++;
305
306         memset(s, 0xff, end-s);
307
308         return entry;
309 }
310
311
312 /** Generates a binary-coded decimal representation of an integer in the range [0, 99] */
313 static inline uint8_t bcd(uint8_t v) {
314         return 0x10 * (v/10) + v%10;
315 }
316
317
318 /** Generates the soft-version partition */
319 static struct image_partition_entry make_soft_version(uint32_t rev) {
320         struct image_partition_entry entry = alloc_image_partition("soft-version", sizeof(struct soft_version));
321         struct soft_version *s = (struct soft_version *)entry.data;
322
323         time_t t;
324
325         if (time(&t) == (time_t)(-1))
326                 error(1, errno, "time");
327
328         struct tm *tm = localtime(&t);
329
330         s->magic = htonl(0x0000000c);
331         s->zero = 0;
332         s->pad1 = 0xff;
333
334         s->version_major = 0;
335         s->version_minor = 0;
336         s->version_patch = 0;
337
338         s->year_hi = bcd((1900+tm->tm_year)/100);
339         s->year_lo = bcd(tm->tm_year%100);
340         s->month = bcd(tm->tm_mon+1);
341         s->day = bcd(tm->tm_mday);
342         s->rev = htonl(rev);
343
344         s->pad2 = 0xff;
345
346         return entry;
347 }
348
349 /** Generates the support-list partition */
350 static struct image_partition_entry make_support_list(const char *support_list, bool trailzero) {
351         size_t len = strlen(support_list);
352         struct image_partition_entry entry = alloc_image_partition("support-list", len + 9);
353
354         put32(entry.data, len);
355         memset(entry.data+4, 0, 4);
356         memcpy(entry.data+8, support_list, len);
357         entry.data[len+8] = trailzero ? '\x00' : '\xff';
358
359         return entry;
360 }
361
362 /** Creates a new image partition with an arbitrary name from a file */
363 static struct image_partition_entry read_file(const char *part_name, const char *filename, bool add_jffs2_eof) {
364         struct stat statbuf;
365
366         if (stat(filename, &statbuf) < 0)
367                 error(1, errno, "unable to stat file `%s'", filename);
368
369         size_t len = statbuf.st_size;
370
371         if (add_jffs2_eof)
372                 len = ALIGN(len, 0x10000) + sizeof(jffs2_eof_mark);
373
374         struct image_partition_entry entry = alloc_image_partition(part_name, len);
375
376         FILE *file = fopen(filename, "rb");
377         if (!file)
378                 error(1, errno, "unable to open file `%s'", filename);
379
380         if (fread(entry.data, statbuf.st_size, 1, file) != 1)
381                 error(1, errno, "unable to read file `%s'", filename);
382
383         if (add_jffs2_eof) {
384                 uint8_t *eof = entry.data + statbuf.st_size, *end = entry.data+entry.size;
385
386                 memset(eof, 0xff, end - eof - sizeof(jffs2_eof_mark));
387                 memcpy(end - sizeof(jffs2_eof_mark), jffs2_eof_mark, sizeof(jffs2_eof_mark));
388         }
389
390         fclose(file);
391
392         return entry;
393 }
394
395
396 /**
397    Copies a list of image partitions into an image buffer and generates the image partition table while doing so
398
399    Example image partition table:
400
401      fwup-ptn partition-table base 0x00800 size 0x00800
402      fwup-ptn os-image base 0x01000 size 0x113b45
403      fwup-ptn file-system base 0x114b45 size 0x1d0004
404      fwup-ptn support-list base 0x2e4b49 size 0x000d1
405
406    Each line of the partition table is terminated with the bytes 09 0d 0a ("\t\r\n"),
407    the end of the partition table is marked with a zero byte.
408
409    The firmware image must contain at least the partition-table and support-list partitions
410    to be accepted. There aren't any alignment constraints for the image partitions.
411
412    The partition-table partition contains the actual flash layout; partitions
413    from the image partition table are mapped to the corresponding flash partitions during
414    the firmware upgrade. The support-list partition contains a list of devices supported by
415    the firmware image.
416
417    The base offsets in the firmware partition table are relative to the end
418    of the vendor information block, so the partition-table partition will
419    actually start at offset 0x1814 of the image.
420
421    I think partition-table must be the first partition in the firmware image.
422 */
423 static void put_partitions(uint8_t *buffer, const struct image_partition_entry *parts) {
424         size_t i;
425         char *image_pt = (char *)buffer, *end = image_pt + 0x800;
426
427         size_t base = 0x800;
428         for (i = 0; parts[i].name; i++) {
429                 memcpy(buffer + base, parts[i].data, parts[i].size);
430
431                 size_t len = end-image_pt;
432                 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);
433
434                 if (w > len-1)
435                         error(1, 0, "image partition table overflow?");
436
437                 image_pt += w;
438
439                 base += parts[i].size;
440         }
441
442         image_pt++;
443
444         memset(image_pt, 0xff, end-image_pt);
445 }
446
447 /** Generates and writes the image MD5 checksum */
448 static void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) {
449         MD5_CTX ctx;
450
451         MD5_Init(&ctx);
452         MD5_Update(&ctx, md5_salt, (unsigned int)sizeof(md5_salt));
453         MD5_Update(&ctx, buffer, len);
454         MD5_Final(md5, &ctx);
455 }
456
457
458 /**
459    Generates the firmware image in factory format
460
461    Image format:
462
463      Bytes (hex)  Usage
464      -----------  -----
465      0000-0003    Image size (4 bytes, big endian)
466      0004-0013    MD5 hash (hash of a 16 byte salt and the image data starting with byte 0x14)
467      0014-0017    Vendor information length (without padding) (4 bytes, big endian)
468      0018-1013    Vendor information (4092 bytes, padded with 0xff; there seem to be older
469                   (VxWorks-based) TP-LINK devices which use a smaller vendor information block)
470      1014-1813    Image partition table (2048 bytes, padded with 0xff)
471      1814-xxxx    Firmware partitions
472 */
473 static void * generate_factory_image(const char *vendor, const struct image_partition_entry *parts, size_t *len) {
474         *len = 0x1814;
475
476         size_t i;
477         for (i = 0; parts[i].name; i++)
478                 *len += parts[i].size;
479
480         uint8_t *image = malloc(*len);
481         if (!image)
482                 error(1, errno, "malloc");
483
484         put32(image, *len);
485
486         size_t vendor_len = strlen(vendor);
487         put32(image+0x14, vendor_len);
488         memcpy(image+0x18, vendor, vendor_len);
489         memset(image+0x18+vendor_len, 0xff, 4092-vendor_len);
490
491         put_partitions(image + 0x1014, parts);
492         put_md5(image+0x04, image+0x14, *len-0x14);
493
494         return image;
495 }
496
497 /**
498    Generates the firmware image in sysupgrade format
499
500    This makes some assumptions about the provided flash and image partition tables and
501    should be generalized when TP-LINK starts building its safeloader into hardware with
502    different flash layouts.
503 */
504 static void * generate_sysupgrade_image(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len) {
505         const struct flash_partition_entry *flash_os_image = &flash_parts[5];
506         const struct flash_partition_entry *flash_soft_version = &flash_parts[6];
507         const struct flash_partition_entry *flash_support_list = &flash_parts[7];
508         const struct flash_partition_entry *flash_file_system = &flash_parts[8];
509
510         const struct image_partition_entry *image_os_image = &image_parts[3];
511         const struct image_partition_entry *image_soft_version = &image_parts[1];
512         const struct image_partition_entry *image_support_list = &image_parts[2];
513         const struct image_partition_entry *image_file_system = &image_parts[4];
514
515         assert(strcmp(flash_os_image->name, "os-image") == 0);
516         assert(strcmp(flash_soft_version->name, "soft-version") == 0);
517         assert(strcmp(flash_support_list->name, "support-list") == 0);
518         assert(strcmp(flash_file_system->name, "file-system") == 0);
519
520         assert(strcmp(image_os_image->name, "os-image") == 0);
521         assert(strcmp(image_soft_version->name, "soft-version") == 0);
522         assert(strcmp(image_support_list->name, "support-list") == 0);
523         assert(strcmp(image_file_system->name, "file-system") == 0);
524
525         if (image_os_image->size > flash_os_image->size)
526                 error(1, 0, "kernel image too big (more than %u bytes)", (unsigned)flash_os_image->size);
527         if (image_file_system->size > flash_file_system->size)
528                 error(1, 0, "rootfs image too big (more than %u bytes)", (unsigned)flash_file_system->size);
529
530         *len = flash_file_system->base - flash_os_image->base + image_file_system->size;
531
532         uint8_t *image = malloc(*len);
533         if (!image)
534                 error(1, errno, "malloc");
535
536         memset(image, 0xff, *len);
537
538         memcpy(image, image_os_image->data, image_os_image->size);
539         memcpy(image + flash_soft_version->base - flash_os_image->base, image_soft_version->data, image_soft_version->size);
540         memcpy(image + flash_support_list->base - flash_os_image->base, image_support_list->data, image_support_list->size);
541         memcpy(image + flash_file_system->base - flash_os_image->base, image_file_system->data, image_file_system->size);
542
543         return image;
544 }
545
546 static void * generate_sysupgrade_image_c2600(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len) {
547         const struct flash_partition_entry *flash_os_image = &flash_parts[11];
548         const struct flash_partition_entry *flash_file_system = &flash_parts[12];
549
550         const struct image_partition_entry *image_os_image = &image_parts[3];
551         const struct image_partition_entry *image_file_system = &image_parts[4];
552
553         assert(strcmp(flash_os_image->name, "os-image") == 0);
554         assert(strcmp(flash_file_system->name, "file-system") == 0);
555
556         assert(strcmp(image_os_image->name, "os-image") == 0);
557         assert(strcmp(image_file_system->name, "file-system") == 0);
558
559         if (image_os_image->size > flash_os_image->size)
560                 error(1, 0, "kernel image too big (more than %u bytes)", (unsigned)flash_os_image->size);
561         if (image_file_system->size > flash_file_system->size)
562                 error(1, 0, "rootfs image too big (more than %u bytes)", (unsigned)flash_file_system->size);
563
564         *len = flash_file_system->base - flash_os_image->base + image_file_system->size;
565
566         uint8_t *image = malloc(*len);
567         if (!image)
568                 error(1, errno, "malloc");
569
570         memset(image, 0xff, *len);
571
572         memcpy(image, image_os_image->data, image_os_image->size);
573         memcpy(image + flash_file_system->base - flash_os_image->base, image_file_system->data, image_file_system->size);
574
575         return image;
576 }
577 static void *generate_sysupgrade_image_eap120(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len)
578 {
579         const struct flash_partition_entry *flash_os_image = &flash_parts[6];
580         const struct flash_partition_entry *flash_file_system = &flash_parts[7];
581
582         const struct image_partition_entry *image_os_image = &image_parts[3];
583         const struct image_partition_entry *image_file_system = &image_parts[4];
584
585         assert(strcmp(flash_os_image->name, "os-image") == 0);
586         assert(strcmp(flash_file_system->name, "file-system") == 0);
587
588         assert(strcmp(image_os_image->name, "os-image") == 0);
589         assert(strcmp(image_file_system->name, "file-system") == 0);
590
591         if (image_os_image->size > flash_os_image->size)
592                 error(1, 0, "kernel image too big (more than %u bytes)", (unsigned)flash_os_image->size);
593         if (image_file_system->size > flash_file_system->size)
594                 error(1, 0, "rootfs image too big (more than %u bytes)", (unsigned)flash_file_system->size);
595
596         *len = flash_file_system->base - flash_os_image->base + image_file_system->size;
597
598         uint8_t *image = malloc(*len);
599         if (!image)
600                 error(1, errno, "malloc");
601
602         memset(image, 0xff, *len);
603         memcpy(image, image_os_image->data, image_os_image->size);
604         memcpy(image + flash_file_system->base - flash_os_image->base, image_file_system->data, image_file_system->size);
605
606         return image;
607 }
608
609 struct device_info cpe210_info = {
610         .vendor = cpe510_vendor,
611         .support_list = cpe210_support_list,
612         .partitions = cpe510_partitions,
613         .generate_sysupgrade_image = &generate_sysupgrade_image,
614 };
615
616 struct device_info cpe510_info = {
617         .vendor = cpe510_vendor,
618         .support_list = cpe510_support_list,
619         .partitions = cpe510_partitions,
620         .generate_sysupgrade_image = &generate_sysupgrade_image,
621 };
622
623 struct device_info c2600_info = {
624         .vendor = c2600_vendor,
625         .support_list = c2600_support_list,
626         .partitions = c2600_partitions,
627         .generate_sysupgrade_image = &generate_sysupgrade_image_c2600,
628 };
629
630 struct device_info e9_info = {
631         .vendor = c2600_vendor,
632         .support_list = c9_support_list,
633         .partitions = c5_partitions,
634 };
635
636 struct device_info eap120_info = {
637         .vendor = eap120_vendor,
638         .support_list = eap120_support_list,
639         .partitions = eap120_partitions,
640         .generate_sysupgrade_image = &generate_sysupgrade_image_eap120,
641 };
642
643 static void build_image(const char *output,
644                 const char *kernel_image,
645                 const char *rootfs_image,
646                 uint32_t rev,
647                 bool add_jffs2_eof,
648                 bool sysupgrade,
649                 struct device_info *info) {
650         struct image_partition_entry parts[6] = {};
651
652         parts[0] = make_partition_table(info->partitions);
653         parts[1] = make_soft_version(rev);
654         parts[2] = make_support_list(info->support_list, false);
655         parts[3] = read_file("os-image", kernel_image, false);
656         parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof);
657
658         size_t len;
659         void *image;
660         if (sysupgrade)
661                 image = info->generate_sysupgrade_image(info->partitions, parts, &len);
662         else
663                 image = generate_factory_image(info->vendor, parts, &len);
664
665         FILE *file = fopen(output, "wb");
666         if (!file)
667                 error(1, errno, "unable to open output file");
668
669         if (fwrite(image, len, 1, file) != 1)
670                 error(1, 0, "unable to write output file");
671
672         fclose(file);
673
674         free(image);
675
676         size_t i;
677         for (i = 0; parts[i].name; i++)
678                 free_image_partition(parts[i]);
679 }
680
681 /** Usage output */
682 static void usage(const char *argv0) {
683         fprintf(stderr,
684                 "Usage: %s [OPTIONS...]\n"
685                 "\n"
686                 "Options:\n"
687                 "  -B <board>      create image for the board specified with <board>\n"
688                 "  -k <file>       read kernel image from the file <file>\n"
689                 "  -r <file>       read rootfs image from the file <file>\n"
690                 "  -o <file>       write output to the file <file>\n"
691                 "  -V <rev>        sets the revision number to <rev>\n"
692                 "  -j              add jffs2 end-of-filesystem markers\n"
693                 "  -S              create sysupgrade instead of factory image\n"
694                 "  -h              show this help\n",
695                 argv0
696         );
697 };
698
699
700 int main(int argc, char *argv[]) {
701         const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL;
702         bool add_jffs2_eof = false, sysupgrade = false;
703         unsigned rev = 0;
704         struct device_info *info;
705
706         while (true) {
707                 int c;
708
709                 c = getopt(argc, argv, "B:k:r:o:V:jSh");
710                 if (c == -1)
711                         break;
712
713                 switch (c) {
714                 case 'B':
715                         board = optarg;
716                         break;
717
718                 case 'k':
719                         kernel_image = optarg;
720                         break;
721
722                 case 'r':
723                         rootfs_image = optarg;
724                         break;
725
726                 case 'o':
727                         output = optarg;
728                         break;
729
730                 case 'V':
731                         sscanf(optarg, "r%u", &rev);
732                         break;
733
734                 case 'j':
735                         add_jffs2_eof = true;
736                         break;
737
738                 case 'S':
739                         sysupgrade = true;
740                         break;
741
742                 case 'h':
743                         usage(argv[0]);
744                         return 0;
745
746                 default:
747                         usage(argv[0]);
748                         return 1;
749                 }
750         }
751
752         if (!board)
753                 error(1, 0, "no board has been specified");
754         if (!kernel_image)
755                 error(1, 0, "no kernel image has been specified");
756         if (!rootfs_image)
757                 error(1, 0, "no rootfs image has been specified");
758         if (!output)
759                 error(1, 0, "no output filename has been specified");
760
761         if (strcmp(board, "CPE210") == 0)
762                 info = &cpe210_info;
763         else if (strcmp(board, "CPE510") == 0)
764                 info = &cpe510_info;
765         else if (strcmp(board, "C2600") == 0)
766                 info = &c2600_info;
767         else if (strcmp(board, "EAP120") == 0)
768                 info = &eap120_info;
769         else if (strcmp(board, "ARCHERC9") == 0)
770                 info = &e9_info;
771         else
772                 error(1, 0, "unsupported board %s", board);
773
774         build_image(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade, info);
775
776         return 0;
777 }