a1b8c31c70b62256382c72c1e88e6c2e10c25589
[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 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 }