3cdbbabdeac1b44d733e05f5c665601e858e789b
[oweals/openwrt.git] / tools / firmware-utils / src / tplink-safeloader.c
1 /*
2   Copyright (c) 2014, Matthias Schiffer <mschiffer@universe-factory.net>
3   All rights reserved.
4
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions are met:
7
8     1. Redistributions of source code must retain the above copyright notice,
9        this list of conditions and the following disclaimer.
10     2. Redistributions in binary form must reproduce the above copyright notice,
11        this list of conditions and the following disclaimer in the documentation
12        and/or other materials provided with the distribution.
13
14   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26
27 /*
28    tplink-safeloader
29
30    Image generation tool for the TP-LINK SafeLoader as seen on
31    TP-LINK Pharos devices (CPE210/220/510/520)
32 */
33
34
35 #include <assert.h>
36 #include <errno.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdint.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44
45 #include <arpa/inet.h>
46
47 #include <sys/types.h>
48 #include <sys/stat.h>
49
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 /**    The flash partition table for EAP120;
175     it is the same as the one used by the stock images.
176 */
177 static const struct flash_partition_entry eap120_partitions[] = {
178         {"fs-uboot", 0x00000, 0x20000},
179         {"partition-table", 0x20000, 0x02000},
180         {"default-mac", 0x30000, 0x00020},
181         {"support-list", 0x31000, 0x00100},
182         {"product-info", 0x31100, 0x00100},
183         {"soft-version", 0x32000, 0x00100},
184         {"os-image", 0x40000, 0x180000},
185         {"file-system", 0x1c0000, 0x600000},
186         {"user-config", 0x7c0000, 0x10000},
187         {"backup-config", 0x7d0000, 0x10000},
188         {"log", 0x7e0000, 0x10000},
189         {"radio", 0x7f0000, 0x10000},
190         {NULL, 0, 0}
191 };
192
193 /**
194    The support list for CPE210/220
195 */
196 static const char cpe210_support_list[] =
197         "SupportList:\r\n"
198         "CPE210(TP-LINK|UN|N300-2):1.0\r\n"
199         "CPE210(TP-LINK|UN|N300-2):1.1\r\n"
200         "CPE220(TP-LINK|UN|N300-2):1.0\r\n"
201         "CPE220(TP-LINK|UN|N300-2):1.1\r\n";
202 /**
203    The support list for CPE210/220/510/520
204 */
205 static const char cpe510_support_list[] =
206         "SupportList:\r\n"
207         "CPE510(TP-LINK|UN|N300-5):1.0\r\n"
208         "CPE510(TP-LINK|UN|N300-5):1.1\r\n"
209         "CPE520(TP-LINK|UN|N300-5):1.0\r\n"
210         "CPE520(TP-LINK|UN|N300-5):1.1\r\n";
211
212 /**
213    The support list for C2600
214 */
215 static const char c2600_support_list[] =
216         "SupportList:\r\n"
217         "{product_name:Archer C2600,product_ver:1.0.0,special_id:00000000}\r\n";
218
219 /**
220    The support list for EAP120
221 */
222 static const char eap120_support_list[] =
223         "SupportList:\r\n"
224         "EAP120(TP-LINK|UN|N300-2):1.0\r\n";
225
226 #define error(_ret, _errno, _str, ...)                          \
227         do {                                                    \
228                 fprintf(stderr, _str ": %s\n", ## __VA_ARGS__,  \
229                         strerror(_errno));                      \
230                 if (_ret)                                       \
231                         exit(_ret);                             \
232         } while (0)
233
234
235 /** Stores a uint32 as big endian */
236 static inline void put32(uint8_t *buf, uint32_t val) {
237         buf[0] = val >> 24;
238         buf[1] = val >> 16;
239         buf[2] = val >> 8;
240         buf[3] = val;
241 }
242
243 /** Allocates a new image partition */
244 static struct image_partition_entry alloc_image_partition(const char *name, size_t len) {
245         struct image_partition_entry entry = {name, len, malloc(len)};
246         if (!entry.data)
247                 error(1, errno, "malloc");
248
249         return entry;
250 }
251
252 /** Frees an image partition */
253 static void free_image_partition(struct image_partition_entry entry) {
254         free(entry.data);
255 }
256
257 /** Generates the partition-table partition */
258 static struct image_partition_entry make_partition_table(const struct flash_partition_entry *p) {
259         struct image_partition_entry entry = alloc_image_partition("partition-table", 0x800);
260
261         char *s = (char *)entry.data, *end = (char *)(s+entry.size);
262
263         *(s++) = 0x00;
264         *(s++) = 0x04;
265         *(s++) = 0x00;
266         *(s++) = 0x00;
267
268         size_t i;
269         for (i = 0; p[i].name; i++) {
270                 size_t len = end-s;
271                 size_t w = snprintf(s, len, "partition %s base 0x%05x size 0x%05x\n", p[i].name, p[i].base, p[i].size);
272
273                 if (w > len-1)
274                         error(1, 0, "flash partition table overflow?");
275
276                 s += w;
277         }
278
279         s++;
280
281         memset(s, 0xff, end-s);
282
283         return entry;
284 }
285
286
287 /** Generates a binary-coded decimal representation of an integer in the range [0, 99] */
288 static inline uint8_t bcd(uint8_t v) {
289         return 0x10 * (v/10) + v%10;
290 }
291
292
293 /** Generates the soft-version partition */
294 static struct image_partition_entry make_soft_version(uint32_t rev) {
295         struct image_partition_entry entry = alloc_image_partition("soft-version", sizeof(struct soft_version));
296         struct soft_version *s = (struct soft_version *)entry.data;
297
298         time_t t;
299
300         if (time(&t) == (time_t)(-1))
301                 error(1, errno, "time");
302
303         struct tm *tm = localtime(&t);
304
305         s->magic = htonl(0x0000000c);
306         s->zero = 0;
307         s->pad1 = 0xff;
308
309         s->version_major = 0;
310         s->version_minor = 0;
311         s->version_patch = 0;
312
313         s->year_hi = bcd((1900+tm->tm_year)/100);
314         s->year_lo = bcd(tm->tm_year%100);
315         s->month = bcd(tm->tm_mon+1);
316         s->day = bcd(tm->tm_mday);
317         s->rev = htonl(rev);
318
319         s->pad2 = 0xff;
320
321         return entry;
322 }
323
324 /** Generates the support-list partition */
325 static struct image_partition_entry make_support_list(const char *support_list, bool trailzero) {
326         size_t len = strlen(support_list);
327         struct image_partition_entry entry = alloc_image_partition("support-list", len + 9);
328
329         put32(entry.data, len);
330         memset(entry.data+4, 0, 4);
331         memcpy(entry.data+8, support_list, len);
332         entry.data[len+8] = trailzero ? '\x00' : '\xff';
333
334         return entry;
335 }
336
337 /** Creates a new image partition with an arbitrary name from a file */
338 static struct image_partition_entry read_file(const char *part_name, const char *filename, bool add_jffs2_eof) {
339         struct stat statbuf;
340
341         if (stat(filename, &statbuf) < 0)
342                 error(1, errno, "unable to stat file `%s'", filename);
343
344         size_t len = statbuf.st_size;
345
346         if (add_jffs2_eof)
347                 len = ALIGN(len, 0x10000) + sizeof(jffs2_eof_mark);
348
349         struct image_partition_entry entry = alloc_image_partition(part_name, len);
350
351         FILE *file = fopen(filename, "rb");
352         if (!file)
353                 error(1, errno, "unable to open file `%s'", filename);
354
355         if (fread(entry.data, statbuf.st_size, 1, file) != 1)
356                 error(1, errno, "unable to read file `%s'", filename);
357
358         if (add_jffs2_eof) {
359                 uint8_t *eof = entry.data + statbuf.st_size, *end = entry.data+entry.size;
360
361                 memset(eof, 0xff, end - eof - sizeof(jffs2_eof_mark));
362                 memcpy(end - sizeof(jffs2_eof_mark), jffs2_eof_mark, sizeof(jffs2_eof_mark));
363         }
364
365         fclose(file);
366
367         return entry;
368 }
369
370
371 /**
372    Copies a list of image partitions into an image buffer and generates the image partition table while doing so
373
374    Example image partition table:
375
376      fwup-ptn partition-table base 0x00800 size 0x00800
377      fwup-ptn os-image base 0x01000 size 0x113b45
378      fwup-ptn file-system base 0x114b45 size 0x1d0004
379      fwup-ptn support-list base 0x2e4b49 size 0x000d1
380
381    Each line of the partition table is terminated with the bytes 09 0d 0a ("\t\r\n"),
382    the end of the partition table is marked with a zero byte.
383
384    The firmware image must contain at least the partition-table and support-list partitions
385    to be accepted. There aren't any alignment constraints for the image partitions.
386
387    The partition-table partition contains the actual flash layout; partitions
388    from the image partition table are mapped to the corresponding flash partitions during
389    the firmware upgrade. The support-list partition contains a list of devices supported by
390    the firmware image.
391
392    The base offsets in the firmware partition table are relative to the end
393    of the vendor information block, so the partition-table partition will
394    actually start at offset 0x1814 of the image.
395
396    I think partition-table must be the first partition in the firmware image.
397 */
398 static void put_partitions(uint8_t *buffer, const struct image_partition_entry *parts) {
399         size_t i;
400         char *image_pt = (char *)buffer, *end = image_pt + 0x800;
401
402         size_t base = 0x800;
403         for (i = 0; parts[i].name; i++) {
404                 memcpy(buffer + base, parts[i].data, parts[i].size);
405
406                 size_t len = end-image_pt;
407                 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);
408
409                 if (w > len-1)
410                         error(1, 0, "image partition table overflow?");
411
412                 image_pt += w;
413
414                 base += parts[i].size;
415         }
416
417         image_pt++;
418
419         memset(image_pt, 0xff, end-image_pt);
420 }
421
422 /** Generates and writes the image MD5 checksum */
423 static void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) {
424         MD5_CTX ctx;
425
426         MD5_Init(&ctx);
427         MD5_Update(&ctx, md5_salt, (unsigned int)sizeof(md5_salt));
428         MD5_Update(&ctx, buffer, len);
429         MD5_Final(md5, &ctx);
430 }
431
432
433 /**
434    Generates the firmware image in factory format
435
436    Image format:
437
438      Bytes (hex)  Usage
439      -----------  -----
440      0000-0003    Image size (4 bytes, big endian)
441      0004-0013    MD5 hash (hash of a 16 byte salt and the image data starting with byte 0x14)
442      0014-0017    Vendor information length (without padding) (4 bytes, big endian)
443      0018-1013    Vendor information (4092 bytes, padded with 0xff; there seem to be older
444                   (VxWorks-based) TP-LINK devices which use a smaller vendor information block)
445      1014-1813    Image partition table (2048 bytes, padded with 0xff)
446      1814-xxxx    Firmware partitions
447 */
448 static void * generate_factory_image(const char *vendor, const struct image_partition_entry *parts, size_t *len) {
449         *len = 0x1814;
450
451         size_t i;
452         for (i = 0; parts[i].name; i++)
453                 *len += parts[i].size;
454
455         uint8_t *image = malloc(*len);
456         if (!image)
457                 error(1, errno, "malloc");
458
459         put32(image, *len);
460
461         size_t vendor_len = strlen(vendor);
462         put32(image+0x14, vendor_len);
463         memcpy(image+0x18, vendor, vendor_len);
464         memset(image+0x18+vendor_len, 0xff, 4092-vendor_len);
465
466         put_partitions(image + 0x1014, parts);
467         put_md5(image+0x04, image+0x14, *len-0x14);
468
469         return image;
470 }
471
472 /**
473    Generates the firmware image in sysupgrade format
474
475    This makes some assumptions about the provided flash and image partition tables and
476    should be generalized when TP-LINK starts building its safeloader into hardware with
477    different flash layouts.
478 */
479 static void * generate_sysupgrade_image(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len) {
480         const struct flash_partition_entry *flash_os_image = &flash_parts[5];
481         const struct flash_partition_entry *flash_soft_version = &flash_parts[6];
482         const struct flash_partition_entry *flash_support_list = &flash_parts[7];
483         const struct flash_partition_entry *flash_file_system = &flash_parts[8];
484
485         const struct image_partition_entry *image_os_image = &image_parts[3];
486         const struct image_partition_entry *image_soft_version = &image_parts[1];
487         const struct image_partition_entry *image_support_list = &image_parts[2];
488         const struct image_partition_entry *image_file_system = &image_parts[4];
489
490         assert(strcmp(flash_os_image->name, "os-image") == 0);
491         assert(strcmp(flash_soft_version->name, "soft-version") == 0);
492         assert(strcmp(flash_support_list->name, "support-list") == 0);
493         assert(strcmp(flash_file_system->name, "file-system") == 0);
494
495         assert(strcmp(image_os_image->name, "os-image") == 0);
496         assert(strcmp(image_soft_version->name, "soft-version") == 0);
497         assert(strcmp(image_support_list->name, "support-list") == 0);
498         assert(strcmp(image_file_system->name, "file-system") == 0);
499
500         if (image_os_image->size > flash_os_image->size)
501                 error(1, 0, "kernel image too big (more than %u bytes)", (unsigned)flash_os_image->size);
502         if (image_file_system->size > flash_file_system->size)
503                 error(1, 0, "rootfs image too big (more than %u bytes)", (unsigned)flash_file_system->size);
504
505         *len = flash_file_system->base - flash_os_image->base + image_file_system->size;
506
507         uint8_t *image = malloc(*len);
508         if (!image)
509                 error(1, errno, "malloc");
510
511         memset(image, 0xff, *len);
512
513         memcpy(image, image_os_image->data, image_os_image->size);
514         memcpy(image + flash_soft_version->base - flash_os_image->base, image_soft_version->data, image_soft_version->size);
515         memcpy(image + flash_support_list->base - flash_os_image->base, image_support_list->data, image_support_list->size);
516         memcpy(image + flash_file_system->base - flash_os_image->base, image_file_system->data, image_file_system->size);
517
518         return image;
519 }
520
521 static void * generate_sysupgrade_image_c2600(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len) {
522         const struct flash_partition_entry *flash_os_image = &flash_parts[11];
523         const struct flash_partition_entry *flash_file_system = &flash_parts[12];
524
525         const struct image_partition_entry *image_os_image = &image_parts[3];
526         const struct image_partition_entry *image_file_system = &image_parts[4];
527
528         assert(strcmp(flash_os_image->name, "os-image") == 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_file_system->name, "file-system") == 0);
533
534         if (image_os_image->size > flash_os_image->size)
535                 error(1, 0, "kernel image too big (more than %u bytes)", (unsigned)flash_os_image->size);
536         if (image_file_system->size > flash_file_system->size)
537                 error(1, 0, "rootfs image too big (more than %u bytes)", (unsigned)flash_file_system->size);
538
539         *len = flash_file_system->base - flash_os_image->base + image_file_system->size;
540
541         uint8_t *image = malloc(*len);
542         if (!image)
543                 error(1, errno, "malloc");
544
545         memset(image, 0xff, *len);
546
547         memcpy(image, image_os_image->data, image_os_image->size);
548         memcpy(image + flash_file_system->base - flash_os_image->base, image_file_system->data, image_file_system->size);
549
550         return image;
551 }
552 static void *generate_sysupgrade_image_eap120(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len)
553 {
554         const struct flash_partition_entry *flash_os_image = &flash_parts[6];
555         const struct flash_partition_entry *flash_file_system = &flash_parts[7];
556
557         const struct image_partition_entry *image_os_image = &image_parts[3];
558         const struct image_partition_entry *image_file_system = &image_parts[4];
559
560         assert(strcmp(flash_os_image->name, "os-image") == 0);
561         assert(strcmp(flash_file_system->name, "file-system") == 0);
562
563         assert(strcmp(image_os_image->name, "os-image") == 0);
564         assert(strcmp(image_file_system->name, "file-system") == 0);
565
566         if (image_os_image->size > flash_os_image->size)
567                 error(1, 0, "kernel image too big (more than %u bytes)", (unsigned)flash_os_image->size);
568         if (image_file_system->size > flash_file_system->size)
569                 error(1, 0, "rootfs image too big (more than %u bytes)", (unsigned)flash_file_system->size);
570
571         *len = flash_file_system->base - flash_os_image->base + image_file_system->size;
572
573         uint8_t *image = malloc(*len);
574         if (!image)
575                 error(1, errno, "malloc");
576
577         memset(image, 0xff, *len);
578         memcpy(image, image_os_image->data, image_os_image->size);
579         memcpy(image + flash_file_system->base - flash_os_image->base, image_file_system->data, image_file_system->size);
580
581         return image;
582 }
583
584 struct device_info cpe210_info = {
585         .vendor = cpe510_vendor,
586         .support_list = cpe210_support_list,
587         .partitions = cpe510_partitions,
588         .generate_sysupgrade_image = &generate_sysupgrade_image,
589 };
590
591 struct device_info cpe510_info = {
592         .vendor = cpe510_vendor,
593         .support_list = cpe510_support_list,
594         .partitions = cpe510_partitions,
595         .generate_sysupgrade_image = &generate_sysupgrade_image,
596 };
597
598 struct device_info c2600_info = {
599         .vendor = c2600_vendor,
600         .support_list = c2600_support_list,
601         .partitions = c2600_partitions,
602         .generate_sysupgrade_image = &generate_sysupgrade_image_c2600,
603 };
604
605 struct device_info eap120_info = {
606         .vendor = eap120_vendor,
607         .support_list = eap120_support_list,
608         .partitions = eap120_partitions,
609         .generate_sysupgrade_image = &generate_sysupgrade_image_eap120,
610 };
611
612 static void build_image(const char *output,
613                 const char *kernel_image,
614                 const char *rootfs_image,
615                 uint32_t rev,
616                 bool add_jffs2_eof,
617                 bool sysupgrade,
618                 struct device_info *info) {
619         struct image_partition_entry parts[6] = {};
620
621         parts[0] = make_partition_table(info->partitions);
622         parts[1] = make_soft_version(rev);
623         parts[2] = make_support_list(info->support_list, false);
624         parts[3] = read_file("os-image", kernel_image, false);
625         parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof);
626
627         size_t len;
628         void *image;
629         if (sysupgrade)
630                 image = info->generate_sysupgrade_image(info->partitions, parts, &len);
631         else
632                 image = generate_factory_image(info->vendor, parts, &len);
633
634         FILE *file = fopen(output, "wb");
635         if (!file)
636                 error(1, errno, "unable to open output file");
637
638         if (fwrite(image, len, 1, file) != 1)
639                 error(1, 0, "unable to write output file");
640
641         fclose(file);
642
643         free(image);
644
645         size_t i;
646         for (i = 0; parts[i].name; i++)
647                 free_image_partition(parts[i]);
648 }
649
650 /** Usage output */
651 static void usage(const char *argv0) {
652         fprintf(stderr,
653                 "Usage: %s [OPTIONS...]\n"
654                 "\n"
655                 "Options:\n"
656                 "  -B <board>      create image for the board specified with <board>\n"
657                 "  -k <file>       read kernel image from the file <file>\n"
658                 "  -r <file>       read rootfs image from the file <file>\n"
659                 "  -o <file>       write output to the file <file>\n"
660                 "  -V <rev>        sets the revision number to <rev>\n"
661                 "  -j              add jffs2 end-of-filesystem markers\n"
662                 "  -S              create sysupgrade instead of factory image\n"
663                 "  -h              show this help\n",
664                 argv0
665         );
666 };
667
668
669 int main(int argc, char *argv[]) {
670         const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL;
671         bool add_jffs2_eof = false, sysupgrade = false;
672         unsigned rev = 0;
673         struct device_info *info;
674
675         while (true) {
676                 int c;
677
678                 c = getopt(argc, argv, "B:k:r:o:V:jSh");
679                 if (c == -1)
680                         break;
681
682                 switch (c) {
683                 case 'B':
684                         board = optarg;
685                         break;
686
687                 case 'k':
688                         kernel_image = optarg;
689                         break;
690
691                 case 'r':
692                         rootfs_image = optarg;
693                         break;
694
695                 case 'o':
696                         output = optarg;
697                         break;
698
699                 case 'V':
700                         sscanf(optarg, "r%u", &rev);
701                         break;
702
703                 case 'j':
704                         add_jffs2_eof = true;
705                         break;
706
707                 case 'S':
708                         sysupgrade = true;
709                         break;
710
711                 case 'h':
712                         usage(argv[0]);
713                         return 0;
714
715                 default:
716                         usage(argv[0]);
717                         return 1;
718                 }
719         }
720
721         if (!board)
722                 error(1, 0, "no board has been specified");
723         if (!kernel_image)
724                 error(1, 0, "no kernel image has been specified");
725         if (!rootfs_image)
726                 error(1, 0, "no rootfs image has been specified");
727         if (!output)
728                 error(1, 0, "no output filename has been specified");
729
730         if (strcmp(board, "CPE210") == 0)
731                 info = &cpe210_info;
732         else if (strcmp(board, "CPE510") == 0)
733                 info = &cpe510_info;
734         else if (strcmp(board, "C2600") == 0)
735                 info = &c2600_info;
736         else if (strcmp(board, "EAP120") == 0)
737                 info = &eap120_info;
738         else
739                 error(1, 0, "unsupported board %s", board);
740
741         build_image(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade, info);
742
743         return 0;
744 }