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