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