firmware-utils: tplink-safeloader: use one function for generating images
[openwrt/staging/wigyori.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 }