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