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