firmware-utils: tplink-safeloader: add struct device_info
[librecmc/librecmc.git] / tools / firmware-utils / src / tplink-safeloader.c
1 /*
2   Copyright (c) 2014, Matthias Schiffer <mschiffer@universe-factory.net>
3   All rights reserved.
4
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions are met:
7
8     1. Redistributions of source code must retain the above copyright notice,
9        this list of conditions and the following disclaimer.
10     2. Redistributions in binary form must reproduce the above copyright notice,
11        this list of conditions and the following disclaimer in the documentation
12        and/or other materials provided with the distribution.
13
14   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26
27 /*
28    tplink-safeloader
29
30    Image generation tool for the TP-LINK SafeLoader as seen on
31    TP-LINK Pharos devices (CPE210/220/510/520)
32 */
33
34
35 #include <assert.h>
36 #include <errno.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdint.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44
45 #include <arpa/inet.h>
46
47 #include <sys/types.h>
48 #include <sys/stat.h>
49
50 #include "md5.h"
51
52
53 #define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); })
54
55
56 /** An image partition table entry */
57 struct image_partition_entry {
58         const char *name;
59         size_t size;
60         uint8_t *data;
61 };
62
63 /** A flash partition table entry */
64 struct flash_partition_entry {
65         const char *name;
66         uint32_t base;
67         uint32_t size;
68 };
69
70 struct device_info {
71         const char *vendor;
72         const char *support_list;
73         const struct flash_partition_entry *partitions;
74         void *(*generate_sysupgrade_image)(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len);
75 };
76
77 /** The content of the soft-version structure */
78 struct __attribute__((__packed__)) soft_version {
79         uint32_t magic;
80         uint32_t zero;
81         uint8_t pad1;
82         uint8_t version_major;
83         uint8_t version_minor;
84         uint8_t version_patch;
85         uint8_t year_hi;
86         uint8_t year_lo;
87         uint8_t month;
88         uint8_t day;
89         uint32_t rev;
90         uint8_t pad2;
91 };
92
93
94 static const uint8_t jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde};
95
96
97 /**
98    Salt for the MD5 hash
99
100    Fortunately, TP-LINK seems to use the same salt for most devices which use
101    the new image format.
102 */
103 static const uint8_t md5_salt[16] = {
104         0x7a, 0x2b, 0x15, 0xed,
105         0x9b, 0x98, 0x59, 0x6d,
106         0xe5, 0x04, 0xab, 0x44,
107         0xac, 0x2a, 0x9f, 0x4e,
108 };
109
110
111 /** Vendor information for CPE210/220/510/520 */
112 static const char cpe510_vendor[] = "CPE510(TP-LINK|UN|N300-5):1.0\r\n";
113
114 /** Vendor information for C2600 */
115 static const char c2600_vendor[] = "";
116
117 /** Vendor information for EAP120 */
118 static const char eap120_vendor[] = "EAP120(TP-LINK|UN|N300-2):1.0\r\n";
119
120 /**
121     The flash partition table for CPE210/220/510/520;
122     it is the same as the one used by the stock images.
123 */
124 static const struct flash_partition_entry cpe510_partitions[] = {
125         {"fs-uboot", 0x00000, 0x20000},
126         {"partition-table", 0x20000, 0x02000},
127         {"default-mac", 0x30000, 0x00020},
128         {"product-info", 0x31100, 0x00100},
129         {"signature", 0x32000, 0x00400},
130         {"os-image", 0x40000, 0x170000},
131         {"soft-version", 0x1b0000, 0x00100},
132         {"support-list", 0x1b1000, 0x00400},
133         {"file-system", 0x1c0000, 0x600000},
134         {"user-config", 0x7c0000, 0x10000},
135         {"default-config", 0x7d0000, 0x10000},
136         {"log", 0x7e0000, 0x10000},
137         {"radio", 0x7f0000, 0x10000},
138         {NULL, 0, 0}
139 };
140
141 /**
142     The flash partition table for C2600;
143     it is the same as the one used by the stock images.
144 */
145 static const struct flash_partition_entry c2600_partitions[] = {
146         {"SBL1", 0x00000, 0x20000},
147         {"MIBIB", 0x20000, 0x20000},
148         {"SBL2", 0x40000, 0x20000},
149         {"SBL3", 0x60000, 0x30000},
150         {"DDRCONFIG", 0x90000, 0x10000},
151         {"SSD", 0xa0000, 0x10000},
152         {"TZ", 0xb0000, 0x30000},
153         {"RPM", 0xe0000, 0x20000},
154         {"fs-uboot", 0x100000, 0x70000},
155         {"uboot-env", 0x170000, 0x40000},
156         {"radio", 0x1b0000, 0x40000},
157         {"os-image", 0x1f0000, 0x200000},
158         {"file-system", 0x3f0000, 0x1b00000},
159         {"default-mac", 0x1ef0000, 0x00200},
160         {"pin", 0x1ef0200, 0x00200},
161         {"product-info", 0x1ef0400, 0x0fc00},
162         {"partition-table", 0x1f00000, 0x10000},
163         {"soft-version", 0x1f10000, 0x10000},
164         {"support-list", 0x1f20000, 0x10000},
165         {"profile", 0x1f30000, 0x10000},
166         {"default-config", 0x1f40000, 0x10000},
167         {"user-config", 0x1f50000, 0x40000},
168         {"qos-db", 0x1f90000, 0x40000},
169         {"usb-config", 0x1fd0000, 0x10000},
170         {"log", 0x1fe0000, 0x20000},
171         {NULL, 0, 0}
172 };
173
174 /**    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 eap120_info = {
585         .vendor = eap120_vendor,
586         .support_list = eap120_support_list,
587         .partitions = eap120_partitions,
588         .generate_sysupgrade_image = &generate_sysupgrade_image_eap120,
589 };
590
591 /** Generates an image for CPE210/220/510/520 and writes it to a file */
592 static void do_cpe(const char *output,
593                 const char *kernel_image,
594                 const char *rootfs_image,
595                 uint32_t rev,
596                 bool add_jffs2_eof,
597                 bool sysupgrade,
598                 const char *support_list) {
599         struct image_partition_entry parts[6] = {};
600
601         parts[0] = make_partition_table(cpe510_partitions);
602         parts[1] = make_soft_version(rev);
603         parts[2] = make_support_list(support_list, false);
604         parts[3] = read_file("os-image", kernel_image, false);
605         parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof);
606
607         size_t len;
608         void *image;
609         if (sysupgrade)
610                 image = generate_sysupgrade_image(cpe510_partitions, parts, &len);
611         else
612                 image = generate_factory_image(cpe510_vendor, parts, &len);
613
614         FILE *file = fopen(output, "wb");
615         if (!file)
616                 error(1, errno, "unable to open output file");
617
618         if (fwrite(image, len, 1, file) != 1)
619                 error(1, 0, "unable to write output file");
620
621         fclose(file);
622
623         free(image);
624
625         size_t i;
626         for (i = 0; parts[i].name; i++)
627                 free_image_partition(parts[i]);
628 }
629
630 /** Generates an image for C2600 and writes it to a file */
631 static void do_c2600(const char *output, const char *kernel_image, const char *rootfs_image, uint32_t rev, bool add_jffs2_eof, bool sysupgrade) {
632         struct image_partition_entry parts[6] = {};
633
634         parts[0] = make_partition_table(c2600_partitions);
635         parts[1] = make_soft_version(rev);
636         parts[2] = make_support_list(c2600_support_list, true);
637         parts[3] = read_file("os-image", kernel_image, false);
638         parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof);
639
640         size_t len;
641         void *image;
642         if (sysupgrade)
643                 image = generate_sysupgrade_image_c2600(c2600_partitions, parts, &len);
644         else
645                 image = generate_factory_image(c2600_vendor, parts, &len);
646
647         FILE *file = fopen(output, "wb");
648         if (!file)
649                 error(1, errno, "unable to open output file");
650
651         if (fwrite(image, len, 1, file) != 1)
652                 error(1, 0, "unable to write output file");
653
654         fclose(file);
655
656         free(image);
657
658         size_t i;
659         for (i = 0; parts[i].name; i++)
660                 free_image_partition(parts[i]);
661 }
662
663
664 /** Generates an image for EAP120 and writes it to a file */
665 static void do_eap(const char *output,
666                 const char *kernel_image,
667                 const char *rootfs_image,
668                 uint32_t rev,
669                 bool add_jffs2_eof,
670                 bool sysupgrade,
671                 struct device_info *info) {
672         struct image_partition_entry parts[6] = {};
673
674         parts[0] = make_partition_table(info->partitions);
675         parts[1] = make_soft_version(rev);
676         parts[2] = make_support_list(info->support_list, false);
677         parts[3] = read_file("os-image", kernel_image, false);
678         parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof);
679
680         size_t len;
681         void *image;
682         if (sysupgrade)
683                 image = info->generate_sysupgrade_image(info->partitions, parts, &len);
684         else
685                 image = generate_factory_image(info->vendor, parts, &len);
686
687         FILE *file = fopen(output, "wb");
688         if (!file)
689                 error(1, errno, "unable to open output file");
690
691         if (fwrite(image, len, 1, file) != 1)
692                 error(1, 0, "unable to write output file");
693
694         fclose(file);
695
696         free(image);
697
698         size_t i;
699         for (i = 0; parts[i].name; i++)
700                 free_image_partition(parts[i]);
701 }
702
703 /** Usage output */
704 static void usage(const char *argv0) {
705         fprintf(stderr,
706                 "Usage: %s [OPTIONS...]\n"
707                 "\n"
708                 "Options:\n"
709                 "  -B <board>      create image for the board specified with <board>\n"
710                 "  -k <file>       read kernel image from the file <file>\n"
711                 "  -r <file>       read rootfs image from the file <file>\n"
712                 "  -o <file>       write output to the file <file>\n"
713                 "  -V <rev>        sets the revision number to <rev>\n"
714                 "  -j              add jffs2 end-of-filesystem markers\n"
715                 "  -S              create sysupgrade instead of factory image\n"
716                 "  -h              show this help\n",
717                 argv0
718         );
719 };
720
721
722 int main(int argc, char *argv[]) {
723         const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL;
724         bool add_jffs2_eof = false, sysupgrade = false;
725         unsigned rev = 0;
726
727         while (true) {
728                 int c;
729
730                 c = getopt(argc, argv, "B:k:r:o:V:jSh");
731                 if (c == -1)
732                         break;
733
734                 switch (c) {
735                 case 'B':
736                         board = optarg;
737                         break;
738
739                 case 'k':
740                         kernel_image = optarg;
741                         break;
742
743                 case 'r':
744                         rootfs_image = optarg;
745                         break;
746
747                 case 'o':
748                         output = optarg;
749                         break;
750
751                 case 'V':
752                         sscanf(optarg, "r%u", &rev);
753                         break;
754
755                 case 'j':
756                         add_jffs2_eof = true;
757                         break;
758
759                 case 'S':
760                         sysupgrade = true;
761                         break;
762
763                 case 'h':
764                         usage(argv[0]);
765                         return 0;
766
767                 default:
768                         usage(argv[0]);
769                         return 1;
770                 }
771         }
772
773         if (!board)
774                 error(1, 0, "no board has been specified");
775         if (!kernel_image)
776                 error(1, 0, "no kernel image has been specified");
777         if (!rootfs_image)
778                 error(1, 0, "no rootfs image has been specified");
779         if (!output)
780                 error(1, 0, "no output filename has been specified");
781
782         if (strcmp(board, "CPE210") == 0)
783                 do_cpe(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade, cpe210_support_list);
784         else if (strcmp(board, "CPE510") == 0)
785                 do_cpe(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade, cpe510_support_list);
786         else if (strcmp(board, "C2600") == 0)
787                 do_c2600(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade);
788         else if (strcmp(board, "EAP120") == 0)
789                 do_eap(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade, &eap120_info);
790         else
791                 error(1, 0, "unsupported board %s", board);
792
793         return 0;
794 }