firmware-utils: tplink-safeloader: update support lists for CPE210/510/...
[lede.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         "CPE210(TP-LINK|US|N300-2):1.1\r\n"
221         "CPE210(TP-LINK|EU|N300-2):1.1\r\n"
222         "CPE220(TP-LINK|UN|N300-2):1.1\r\n"
223         "CPE220(TP-LINK|US|N300-2):1.1\r\n"
224         "CPE220(TP-LINK|EU|N300-2):1.1\r\n"
225         ;
226
227 /**
228    The support list for CPE210/220/510/520
229 */
230 static const char cpe510_support_list[] =
231         "SupportList:\r\n"
232         "CPE510(TP-LINK|UN|N300-5):1.0\r\n"
233         "CPE510(TP-LINK|UN|N300-5):1.1\r\n"
234         "CPE510(TP-LINK|UN|N300-5):1.1\r\n"
235         "CPE510(TP-LINK|US|N300-5):1.1\r\n"
236         "CPE510(TP-LINK|EU|N300-5):1.1\r\n"
237         "CPE520(TP-LINK|UN|N300-5):1.1\r\n"
238         "CPE520(TP-LINK|US|N300-5):1.1\r\n"
239         "CPE520(TP-LINK|EU|N300-5):1.1\r\n"
240         ;
241
242 /**
243    The support list for C2600
244 */
245 static const char c2600_support_list[] =
246         "SupportList:\r\n"
247         "{product_name:Archer C2600,product_ver:1.0.0,special_id:00000000}\r\n";
248
249 static const char c9_support_list[] =
250         "SupportList:\n"
251         "{product_name:ArcherC9,"
252         "product_ver:1.0.0,"
253         "special_id:00000000}\n";
254
255 /**
256    The support list for EAP120
257 */
258 static const char eap120_support_list[] =
259         "SupportList:\r\n"
260         "EAP120(TP-LINK|UN|N300-2):1.0\r\n";
261
262 #define error(_ret, _errno, _str, ...)                          \
263         do {                                                    \
264                 fprintf(stderr, _str ": %s\n", ## __VA_ARGS__,  \
265                         strerror(_errno));                      \
266                 if (_ret)                                       \
267                         exit(_ret);                             \
268         } while (0)
269
270
271 /** Stores a uint32 as big endian */
272 static inline void put32(uint8_t *buf, uint32_t val) {
273         buf[0] = val >> 24;
274         buf[1] = val >> 16;
275         buf[2] = val >> 8;
276         buf[3] = val;
277 }
278
279 /** Allocates a new image partition */
280 static struct image_partition_entry alloc_image_partition(const char *name, size_t len) {
281         struct image_partition_entry entry = {name, len, malloc(len)};
282         if (!entry.data)
283                 error(1, errno, "malloc");
284
285         return entry;
286 }
287
288 /** Frees an image partition */
289 static void free_image_partition(struct image_partition_entry entry) {
290         free(entry.data);
291 }
292
293 /** Generates the partition-table partition */
294 static struct image_partition_entry make_partition_table(const struct flash_partition_entry *p) {
295         struct image_partition_entry entry = alloc_image_partition("partition-table", 0x800);
296
297         char *s = (char *)entry.data, *end = (char *)(s+entry.size);
298
299         *(s++) = 0x00;
300         *(s++) = 0x04;
301         *(s++) = 0x00;
302         *(s++) = 0x00;
303
304         size_t i;
305         for (i = 0; p[i].name; i++) {
306                 size_t len = end-s;
307                 size_t w = snprintf(s, len, "partition %s base 0x%05x size 0x%05x\n", p[i].name, p[i].base, p[i].size);
308
309                 if (w > len-1)
310                         error(1, 0, "flash partition table overflow?");
311
312                 s += w;
313         }
314
315         s++;
316
317         memset(s, 0xff, end-s);
318
319         return entry;
320 }
321
322
323 /** Generates a binary-coded decimal representation of an integer in the range [0, 99] */
324 static inline uint8_t bcd(uint8_t v) {
325         return 0x10 * (v/10) + v%10;
326 }
327
328
329 /** Generates the soft-version partition */
330 static struct image_partition_entry make_soft_version(uint32_t rev) {
331         struct image_partition_entry entry = alloc_image_partition("soft-version", sizeof(struct soft_version));
332         struct soft_version *s = (struct soft_version *)entry.data;
333
334         time_t t;
335
336         if (time(&t) == (time_t)(-1))
337                 error(1, errno, "time");
338
339         struct tm *tm = localtime(&t);
340
341         s->magic = htonl(0x0000000c);
342         s->zero = 0;
343         s->pad1 = 0xff;
344
345         s->version_major = 0;
346         s->version_minor = 0;
347         s->version_patch = 0;
348
349         s->year_hi = bcd((1900+tm->tm_year)/100);
350         s->year_lo = bcd(tm->tm_year%100);
351         s->month = bcd(tm->tm_mon+1);
352         s->day = bcd(tm->tm_mday);
353         s->rev = htonl(rev);
354
355         s->pad2 = 0xff;
356
357         return entry;
358 }
359
360 /** Generates the support-list partition */
361 static struct image_partition_entry make_support_list(struct device_info *info) {
362         size_t len = strlen(info->support_list);
363         struct image_partition_entry entry = alloc_image_partition("support-list", len + 9);
364
365         put32(entry.data, len);
366         memset(entry.data+4, 0, 4);
367         memcpy(entry.data+8, info->support_list, len);
368         entry.data[len+8] = info->support_trail;
369
370         return entry;
371 }
372
373 /** Creates a new image partition with an arbitrary name from a file */
374 static struct image_partition_entry read_file(const char *part_name, const char *filename, bool add_jffs2_eof) {
375         struct stat statbuf;
376
377         if (stat(filename, &statbuf) < 0)
378                 error(1, errno, "unable to stat file `%s'", filename);
379
380         size_t len = statbuf.st_size;
381
382         if (add_jffs2_eof)
383                 len = ALIGN(len, 0x10000) + sizeof(jffs2_eof_mark);
384
385         struct image_partition_entry entry = alloc_image_partition(part_name, len);
386
387         FILE *file = fopen(filename, "rb");
388         if (!file)
389                 error(1, errno, "unable to open file `%s'", filename);
390
391         if (fread(entry.data, statbuf.st_size, 1, file) != 1)
392                 error(1, errno, "unable to read file `%s'", filename);
393
394         if (add_jffs2_eof) {
395                 uint8_t *eof = entry.data + statbuf.st_size, *end = entry.data+entry.size;
396
397                 memset(eof, 0xff, end - eof - sizeof(jffs2_eof_mark));
398                 memcpy(end - sizeof(jffs2_eof_mark), jffs2_eof_mark, sizeof(jffs2_eof_mark));
399         }
400
401         fclose(file);
402
403         return entry;
404 }
405
406
407 /**
408    Copies a list of image partitions into an image buffer and generates the image partition table while doing so
409
410    Example image partition table:
411
412      fwup-ptn partition-table base 0x00800 size 0x00800
413      fwup-ptn os-image base 0x01000 size 0x113b45
414      fwup-ptn file-system base 0x114b45 size 0x1d0004
415      fwup-ptn support-list base 0x2e4b49 size 0x000d1
416
417    Each line of the partition table is terminated with the bytes 09 0d 0a ("\t\r\n"),
418    the end of the partition table is marked with a zero byte.
419
420    The firmware image must contain at least the partition-table and support-list partitions
421    to be accepted. There aren't any alignment constraints for the image partitions.
422
423    The partition-table partition contains the actual flash layout; partitions
424    from the image partition table are mapped to the corresponding flash partitions during
425    the firmware upgrade. The support-list partition contains a list of devices supported by
426    the firmware image.
427
428    The base offsets in the firmware partition table are relative to the end
429    of the vendor information block, so the partition-table partition will
430    actually start at offset 0x1814 of the image.
431
432    I think partition-table must be the first partition in the firmware image.
433 */
434 static void put_partitions(uint8_t *buffer, const struct image_partition_entry *parts) {
435         size_t i;
436         char *image_pt = (char *)buffer, *end = image_pt + 0x800;
437
438         size_t base = 0x800;
439         for (i = 0; parts[i].name; i++) {
440                 memcpy(buffer + base, parts[i].data, parts[i].size);
441
442                 size_t len = end-image_pt;
443                 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);
444
445                 if (w > len-1)
446                         error(1, 0, "image partition table overflow?");
447
448                 image_pt += w;
449
450                 base += parts[i].size;
451         }
452
453         image_pt++;
454
455         memset(image_pt, 0xff, end-image_pt);
456 }
457
458 /** Generates and writes the image MD5 checksum */
459 static void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) {
460         MD5_CTX ctx;
461
462         MD5_Init(&ctx);
463         MD5_Update(&ctx, md5_salt, (unsigned int)sizeof(md5_salt));
464         MD5_Update(&ctx, buffer, len);
465         MD5_Final(md5, &ctx);
466 }
467
468
469 /**
470    Generates the firmware image in factory format
471
472    Image format:
473
474      Bytes (hex)  Usage
475      -----------  -----
476      0000-0003    Image size (4 bytes, big endian)
477      0004-0013    MD5 hash (hash of a 16 byte salt and the image data starting with byte 0x14)
478      0014-0017    Vendor information length (without padding) (4 bytes, big endian)
479      0018-1013    Vendor information (4092 bytes, padded with 0xff; there seem to be older
480                   (VxWorks-based) TP-LINK devices which use a smaller vendor information block)
481      1014-1813    Image partition table (2048 bytes, padded with 0xff)
482      1814-xxxx    Firmware partitions
483 */
484 static void * generate_factory_image(const char *vendor, const struct image_partition_entry *parts, size_t *len) {
485         *len = 0x1814;
486
487         size_t i;
488         for (i = 0; parts[i].name; i++)
489                 *len += parts[i].size;
490
491         uint8_t *image = malloc(*len);
492         if (!image)
493                 error(1, errno, "malloc");
494
495         put32(image, *len);
496
497         size_t vendor_len = strlen(vendor);
498         put32(image+0x14, vendor_len);
499         memcpy(image+0x18, vendor, vendor_len);
500         memset(image+0x18+vendor_len, 0xff, 4092-vendor_len);
501
502         put_partitions(image + 0x1014, parts);
503         put_md5(image+0x04, image+0x14, *len-0x14);
504
505         return image;
506 }
507
508 /**
509    Generates the firmware image in sysupgrade format
510
511    This makes some assumptions about the provided flash and image partition tables and
512    should be generalized when TP-LINK starts building its safeloader into hardware with
513    different flash layouts.
514 */
515 static void * generate_sysupgrade_image(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len) {
516         const struct flash_partition_entry *flash_os_image = &flash_parts[5];
517         const struct flash_partition_entry *flash_soft_version = &flash_parts[6];
518         const struct flash_partition_entry *flash_support_list = &flash_parts[7];
519         const struct flash_partition_entry *flash_file_system = &flash_parts[8];
520
521         const struct image_partition_entry *image_os_image = &image_parts[3];
522         const struct image_partition_entry *image_soft_version = &image_parts[1];
523         const struct image_partition_entry *image_support_list = &image_parts[2];
524         const struct image_partition_entry *image_file_system = &image_parts[4];
525
526         assert(strcmp(flash_os_image->name, "os-image") == 0);
527         assert(strcmp(flash_soft_version->name, "soft-version") == 0);
528         assert(strcmp(flash_support_list->name, "support-list") == 0);
529         assert(strcmp(flash_file_system->name, "file-system") == 0);
530
531         assert(strcmp(image_os_image->name, "os-image") == 0);
532         assert(strcmp(image_soft_version->name, "soft-version") == 0);
533         assert(strcmp(image_support_list->name, "support-list") == 0);
534         assert(strcmp(image_file_system->name, "file-system") == 0);
535
536         if (image_os_image->size > flash_os_image->size)
537                 error(1, 0, "kernel image too big (more than %u bytes)", (unsigned)flash_os_image->size);
538         if (image_file_system->size > flash_file_system->size)
539                 error(1, 0, "rootfs image too big (more than %u bytes)", (unsigned)flash_file_system->size);
540
541         *len = flash_file_system->base - flash_os_image->base + image_file_system->size;
542
543         uint8_t *image = malloc(*len);
544         if (!image)
545                 error(1, errno, "malloc");
546
547         memset(image, 0xff, *len);
548
549         memcpy(image, image_os_image->data, image_os_image->size);
550         memcpy(image + flash_soft_version->base - flash_os_image->base, image_soft_version->data, image_soft_version->size);
551         memcpy(image + flash_support_list->base - flash_os_image->base, image_support_list->data, image_support_list->size);
552         memcpy(image + flash_file_system->base - flash_os_image->base, image_file_system->data, image_file_system->size);
553
554         return image;
555 }
556
557 static void * generate_sysupgrade_image_c2600(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len) {
558         const struct flash_partition_entry *flash_os_image = &flash_parts[11];
559         const struct flash_partition_entry *flash_file_system = &flash_parts[12];
560
561         const struct image_partition_entry *image_os_image = &image_parts[3];
562         const struct image_partition_entry *image_file_system = &image_parts[4];
563
564         assert(strcmp(flash_os_image->name, "os-image") == 0);
565         assert(strcmp(flash_file_system->name, "file-system") == 0);
566
567         assert(strcmp(image_os_image->name, "os-image") == 0);
568         assert(strcmp(image_file_system->name, "file-system") == 0);
569
570         if (image_os_image->size > flash_os_image->size)
571                 error(1, 0, "kernel image too big (more than %u bytes)", (unsigned)flash_os_image->size);
572         if (image_file_system->size > flash_file_system->size)
573                 error(1, 0, "rootfs image too big (more than %u bytes)", (unsigned)flash_file_system->size);
574
575         *len = flash_file_system->base - flash_os_image->base + image_file_system->size;
576
577         uint8_t *image = malloc(*len);
578         if (!image)
579                 error(1, errno, "malloc");
580
581         memset(image, 0xff, *len);
582
583         memcpy(image, image_os_image->data, image_os_image->size);
584         memcpy(image + flash_file_system->base - flash_os_image->base, image_file_system->data, image_file_system->size);
585
586         return image;
587 }
588 static void *generate_sysupgrade_image_eap120(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len)
589 {
590         const struct flash_partition_entry *flash_os_image = &flash_parts[6];
591         const struct flash_partition_entry *flash_file_system = &flash_parts[7];
592
593         const struct image_partition_entry *image_os_image = &image_parts[3];
594         const struct image_partition_entry *image_file_system = &image_parts[4];
595
596         assert(strcmp(flash_os_image->name, "os-image") == 0);
597         assert(strcmp(flash_file_system->name, "file-system") == 0);
598
599         assert(strcmp(image_os_image->name, "os-image") == 0);
600         assert(strcmp(image_file_system->name, "file-system") == 0);
601
602         if (image_os_image->size > flash_os_image->size)
603                 error(1, 0, "kernel image too big (more than %u bytes)", (unsigned)flash_os_image->size);
604         if (image_file_system->size > flash_file_system->size)
605                 error(1, 0, "rootfs image too big (more than %u bytes)", (unsigned)flash_file_system->size);
606
607         *len = flash_file_system->base - flash_os_image->base + image_file_system->size;
608
609         uint8_t *image = malloc(*len);
610         if (!image)
611                 error(1, errno, "malloc");
612
613         memset(image, 0xff, *len);
614         memcpy(image, image_os_image->data, image_os_image->size);
615         memcpy(image + flash_file_system->base - flash_os_image->base, image_file_system->data, image_file_system->size);
616
617         return image;
618 }
619
620 struct device_info cpe210_info = {
621         .vendor = cpe510_vendor,
622         .support_list = cpe210_support_list,
623         .support_trail = '\xff',
624         .partitions = cpe510_partitions,
625         .generate_sysupgrade_image = &generate_sysupgrade_image,
626 };
627
628 struct device_info cpe510_info = {
629         .vendor = cpe510_vendor,
630         .support_list = cpe510_support_list,
631         .support_trail = '\xff',
632         .partitions = cpe510_partitions,
633         .generate_sysupgrade_image = &generate_sysupgrade_image,
634 };
635
636 struct device_info c2600_info = {
637         .vendor = c2600_vendor,
638         .support_list = c2600_support_list,
639         .support_trail = '\x00',
640         .partitions = c2600_partitions,
641         .generate_sysupgrade_image = &generate_sysupgrade_image_c2600,
642 };
643
644 struct device_info e9_info = {
645         .vendor = c2600_vendor,
646         .support_list = c9_support_list,
647         .support_trail = '\x00',
648         .partitions = c5_partitions,
649 };
650
651 struct device_info eap120_info = {
652         .vendor = eap120_vendor,
653         .support_list = eap120_support_list,
654         .support_trail = '\xff',
655         .partitions = eap120_partitions,
656         .generate_sysupgrade_image = &generate_sysupgrade_image_eap120,
657 };
658
659 static void build_image(const char *output,
660                 const char *kernel_image,
661                 const char *rootfs_image,
662                 uint32_t rev,
663                 bool add_jffs2_eof,
664                 bool sysupgrade,
665                 struct device_info *info) {
666         struct image_partition_entry parts[6] = {};
667
668         parts[0] = make_partition_table(info->partitions);
669         parts[1] = make_soft_version(rev);
670         parts[2] = make_support_list(info);
671         parts[3] = read_file("os-image", kernel_image, false);
672         parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof);
673
674         size_t len;
675         void *image;
676         if (sysupgrade)
677                 image = info->generate_sysupgrade_image(info->partitions, parts, &len);
678         else
679                 image = generate_factory_image(info->vendor, parts, &len);
680
681         FILE *file = fopen(output, "wb");
682         if (!file)
683                 error(1, errno, "unable to open output file");
684
685         if (fwrite(image, len, 1, file) != 1)
686                 error(1, 0, "unable to write output file");
687
688         fclose(file);
689
690         free(image);
691
692         size_t i;
693         for (i = 0; parts[i].name; i++)
694                 free_image_partition(parts[i]);
695 }
696
697 /** Usage output */
698 static void usage(const char *argv0) {
699         fprintf(stderr,
700                 "Usage: %s [OPTIONS...]\n"
701                 "\n"
702                 "Options:\n"
703                 "  -B <board>      create image for the board specified with <board>\n"
704                 "  -k <file>       read kernel image from the file <file>\n"
705                 "  -r <file>       read rootfs image from the file <file>\n"
706                 "  -o <file>       write output to the file <file>\n"
707                 "  -V <rev>        sets the revision number to <rev>\n"
708                 "  -j              add jffs2 end-of-filesystem markers\n"
709                 "  -S              create sysupgrade instead of factory image\n"
710                 "  -h              show this help\n",
711                 argv0
712         );
713 };
714
715
716 int main(int argc, char *argv[]) {
717         const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL;
718         bool add_jffs2_eof = false, sysupgrade = false;
719         unsigned rev = 0;
720         struct device_info *info;
721
722         while (true) {
723                 int c;
724
725                 c = getopt(argc, argv, "B:k:r:o:V:jSh");
726                 if (c == -1)
727                         break;
728
729                 switch (c) {
730                 case 'B':
731                         board = optarg;
732                         break;
733
734                 case 'k':
735                         kernel_image = optarg;
736                         break;
737
738                 case 'r':
739                         rootfs_image = optarg;
740                         break;
741
742                 case 'o':
743                         output = optarg;
744                         break;
745
746                 case 'V':
747                         sscanf(optarg, "r%u", &rev);
748                         break;
749
750                 case 'j':
751                         add_jffs2_eof = true;
752                         break;
753
754                 case 'S':
755                         sysupgrade = true;
756                         break;
757
758                 case 'h':
759                         usage(argv[0]);
760                         return 0;
761
762                 default:
763                         usage(argv[0]);
764                         return 1;
765                 }
766         }
767
768         if (!board)
769                 error(1, 0, "no board has been specified");
770         if (!kernel_image)
771                 error(1, 0, "no kernel image has been specified");
772         if (!rootfs_image)
773                 error(1, 0, "no rootfs image has been specified");
774         if (!output)
775                 error(1, 0, "no output filename has been specified");
776
777         if (strcmp(board, "CPE210") == 0)
778                 info = &cpe210_info;
779         else if (strcmp(board, "CPE510") == 0)
780                 info = &cpe510_info;
781         else if (strcmp(board, "C2600") == 0)
782                 info = &c2600_info;
783         else if (strcmp(board, "EAP120") == 0)
784                 info = &eap120_info;
785         else if (strcmp(board, "ARCHERC9") == 0)
786                 info = &e9_info;
787         else
788                 error(1, 0, "unsupported board %s", board);
789
790         build_image(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade, info);
791
792         return 0;
793 }