· 7 years ago · Feb 22, 2019, 09:10 PM
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 */
57struct image_partition_entry {
58 const char *name;
59 size_t size;
60 uint8_t *data;
61};
62
63/** A flash partition table entry */
64struct flash_partition_entry {
65 const char *name;
66 uint32_t base;
67 uint32_t size;
68};
69
70
71/** The content of the soft-version structure */
72struct __attribute__((__packed__)) soft_version {
73 uint32_t magic;
74 uint32_t zero;
75 uint8_t pad1;
76 uint8_t version_major;
77 uint8_t version_minor;
78 uint8_t version_patch;
79 uint8_t year_hi;
80 uint8_t year_lo;
81 uint8_t month;
82 uint8_t day;
83 uint32_t rev;
84 uint8_t pad2;
85};
86
87
88static const uint8_t jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde};
89
90
91/**
92 Salt for the MD5 hash
93
94 Fortunately, TP-LINK seems to use the same salt for most devices which use
95 the new image format.
96*/
97static const uint8_t md5_salt[16] = {
98 0x7a, 0x2b, 0x15, 0xed,
99 0x9b, 0x98, 0x59, 0x6d,
100 0xe5, 0x04, 0xab, 0x44,
101 0xac, 0x2a, 0x9f, 0x4e,
102};
103
104
105/** Vendor information for CPE210/220/510/520 */
106static const unsigned char cpe510_vendor[] = "\x00\x00\x00\x1f""CPE510(TP-LINK|UN|N300-5):1.0\r\n";
107static const unsigned char wa855re_vendor[] = ""; // \x00\x00\x00\x1f""WA855REv4(TP-LINK):2017.12.22\r\n";
108
109
110/**
111 The flash partition table for CPE210/220/510/520;
112 it is the same as the one used by the stock images.
113*/
114static const struct flash_partition_entry cpe510_partitions[] = {
115 {"fs-uboot", 0x00000, 0x20000},
116 {"partition-table", 0x20000, 0x02000},
117 {"default-mac", 0x30000, 0x00020},
118 {"product-info", 0x31100, 0x00100},
119 {"signature", 0x32000, 0x00400},
120 {"os-image", 0x40000, 0x170000},
121 {"soft-version", 0x1b0000, 0x00100},
122 {"support-list", 0x1b1000, 0x00400},
123 {"file-system", 0x1c0000, 0x600000},
124 {"user-config", 0x7c0000, 0x10000},
125 {"default-config", 0x7d0000, 0x10000},
126 {"log", 0x7e0000, 0x10000},
127 {"radio", 0x7f0000, 0x10000},
128 {NULL, 0, 0}
129};
130
131static const struct flash_partition_entry wa855re_partitions[] = {
132 {"fs-uboot", 0x00000, 0x20000},
133 {"os-image", 0x20000, 0xe0000},
134 {"file-system", 0x100000, 0x2c2000},
135 {"partition-table", 0x3c2000, 0x02000},
136 {"default-mac", 0x3c4000, 0x00020},
137 {"pin", 0x3c4100, 0x00020},
138 {"product-info", 0x3c5000, 0x01000},
139 {"soft-version", 0x3c6000, 0x01000},
140 {"support-list", 0x3c7000, 0x01000},
141 {"profile", 0x3c8000, 0x08000},
142 {"user-config", 0x3d0000, 0x10000},
143 {"default-config", 0x3e0000, 0x10000},
144 {"radio", 0x3f0000, 0x10000},
145 {NULL, 0, 0}
146};
147
148/**
149 The support list for CPE210/220/510/520
150
151 The stock images also contain strings for two more devices: BS510 and BS210.
152 At the moment, there exists no public information about these devices.
153*/
154static const unsigned char cpe510_support_list[] =
155 "\x00\x00\x00\xc8\x00\x00\x00\x00"
156 "SupportList:\r\n"
157 "CPE510(TP-LINK|UN|N300-5):1.0\r\n"
158 "CPE520(TP-LINK|UN|N300-5):1.0\r\n"
159 "CPE210(TP-LINK|UN|N300-2):1.0\r\n"
160 "CPE220(TP-LINK|UN|N300-2):1.0\r\n"
161 "\r\n\xff";
162
163static const unsigned char wa855re_support_list[] = {
164 0x00, 0x00, 0x01, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x53, 0x75, 0x70, 0x70,
165 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x0a, 0x7b, 0x70, 0x72,
166 0x6f, 0x64, 0x75, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x54,
167 0x4c, 0x2d, 0x57, 0x41, 0x38, 0x35, 0x35, 0x52, 0x45, 0x2c, 0x70, 0x72,
168 0x6f, 0x64, 0x75, 0x63, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x3a, 0x34, 0x2e,
169 0x30, 0x2e, 0x30, 0x2c, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x5f,
170 0x69, 0x64, 0x3a, 0x35, 0x35, 0x35, 0x33, 0x30, 0x30, 0x30, 0x30, 0x7d,
171 0x0a, 0x7b, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x5f, 0x6e, 0x61,
172 0x6d, 0x65, 0x3a, 0x54, 0x4c, 0x2d, 0x57, 0x41, 0x38, 0x35, 0x35, 0x52,
173 0x45, 0x2c, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x5f, 0x76, 0x65,
174 0x72, 0x3a, 0x34, 0x2e, 0x30, 0x2e, 0x30, 0x2c, 0x73, 0x70, 0x65, 0x63,
175 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x3a, 0x34, 0x35, 0x35, 0x35, 0x30,
176 0x30, 0x30, 0x30, 0x7d, 0x0a, 0x7b, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63,
177 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x54, 0x4c, 0x2d, 0x57, 0x41,
178 0x38, 0x35, 0x35, 0x52, 0x45, 0x2c, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63,
179 0x74, 0x5f, 0x76, 0x65, 0x72, 0x3a, 0x34, 0x2e, 0x30, 0x2e, 0x30, 0x2c,
180 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x3a, 0x34,
181 0x32, 0x35, 0x32, 0x30, 0x30, 0x30, 0x30, 0x7d, 0x0a, 0x7b, 0x70, 0x72,
182 0x6f, 0x64, 0x75, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x54,
183 0x4c, 0x2d, 0x57, 0x41, 0x38, 0x35, 0x35, 0x52, 0x45, 0x2c, 0x70, 0x72,
184 0x6f, 0x64, 0x75, 0x63, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x3a, 0x34, 0x2e,
185 0x30, 0x2e, 0x30, 0x2c, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x5f,
186 0x69, 0x64, 0x3a, 0x34, 0x31, 0x35, 0x35, 0x30, 0x30, 0x30, 0x30, 0x7d,
187 0x0a, 0x7b, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x5f, 0x6e, 0x61,
188 0x6d, 0x65, 0x3a, 0x54, 0x4c, 0x2d, 0x57, 0x41, 0x38, 0x35, 0x35, 0x52,
189 0x45, 0x2c, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x5f, 0x76, 0x65,
190 0x72, 0x3a, 0x34, 0x2e, 0x30, 0x2e, 0x30, 0x2c, 0x73, 0x70, 0x65, 0x63,
191 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x3a, 0x34, 0x41, 0x35, 0x30, 0x30,
192 0x30, 0x30, 0x30, 0x7d, 0x0a, 0x7b, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63,
193 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x54, 0x4c, 0x2d, 0x57, 0x41,
194 0x38, 0x35, 0x35, 0x52, 0x45, 0x2c, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63,
195 0x74, 0x5f, 0x76, 0x65, 0x72, 0x3a, 0x34, 0x2e, 0x30, 0x2e, 0x30, 0x2c,
196 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x3a, 0x34,
197 0x35, 0x35, 0x33, 0x30, 0x30, 0x30, 0x30, 0x7d, 0x0a, 0x00
198};
199unsigned int support_list_len = 406;
200
201#define error(_ret, _errno, _str, ...) \
202 do { \
203 fprintf(stderr, _str ": %s\n", ## __VA_ARGS__, \
204 strerror(_errno)); \
205 if (_ret) \
206 exit(_ret); \
207 } while (0)
208
209
210/** Allocates a new image partition */
211struct image_partition_entry alloc_image_partition(const char *name, size_t len) {
212 struct image_partition_entry entry = {name, len, malloc(len)};
213 if (!entry.data)
214 error(1, errno, "malloc");
215
216 return entry;
217}
218
219/** Frees an image partition */
220void free_image_partition(struct image_partition_entry entry) {
221 free(entry.data);
222}
223
224/** Generates the partition-table partition */
225struct image_partition_entry make_partition_table(const struct flash_partition_entry *p) {
226 struct image_partition_entry entry = alloc_image_partition("partition-table", 0x800);
227
228 char *s = (char *)entry.data, *end = (char *)(s+entry.size);
229
230 *(s++) = 0x00;
231 *(s++) = 0x04;
232 *(s++) = 0x00;
233 *(s++) = 0x00;
234
235 size_t i;
236 for (i = 0; p[i].name; i++) {
237 size_t len = end-s;
238 size_t w = snprintf(s, len, "partition %s base 0x%05x size 0x%05x\n", p[i].name, p[i].base, p[i].size);
239
240 if (w > len-1)
241 error(1, 0, "flash partition table overflow?");
242
243 s += w;
244 }
245
246 s++;
247
248 memset(s, 0xff, end-s);
249
250 return entry;
251}
252
253
254/** Generates a binary-coded decimal representation of an integer in the range [0, 99] */
255static inline uint8_t bcd(uint8_t v) {
256 return 0x10 * (v/10) + v%10;
257}
258
259
260/** Generates the soft-version partition */
261struct image_partition_entry make_soft_version(uint32_t rev) {
262 struct image_partition_entry entry = alloc_image_partition("soft-version", sizeof(struct soft_version));
263 struct soft_version *s = (struct soft_version *)entry.data;
264
265 time_t t;
266
267 if (time(&t) == (time_t)(-1))
268 error(1, errno, "time");
269
270 struct tm *tm = localtime(&t);
271
272 s->magic = htonl(0x0000000c);
273 s->zero = 0;
274 s->pad1 = 0xff;
275
276 s->version_major = 0;
277 s->version_minor = 0;
278 s->version_patch = 0;
279
280 s->year_hi = bcd((1900+tm->tm_year)/100);
281 s->year_lo = bcd(tm->tm_year%100);
282 s->month = bcd(tm->tm_mon+1);
283 s->day = bcd(tm->tm_mday);
284 s->rev = htonl(rev);
285
286 s->pad2 = 0xff;
287
288 return entry;
289}
290
291/** Generates the support-list partition */
292struct image_partition_entry make_support_list(const unsigned char *support_list, size_t len) {
293 struct image_partition_entry entry = alloc_image_partition("support-list", len);
294 memcpy(entry.data, support_list, len);
295 return entry;
296}
297
298/** Creates a new image partition with an arbitrary name from a file */
299struct image_partition_entry read_file(const char *part_name, const char *filename, bool add_jffs2_eof) {
300 struct stat statbuf;
301
302 if (stat(filename, &statbuf) < 0)
303 error(1, errno, "unable to stat file `%s'", filename);
304
305 size_t len = statbuf.st_size;
306
307 if (add_jffs2_eof)
308 len = ALIGN(len, 0x10000) + sizeof(jffs2_eof_mark);
309
310 struct image_partition_entry entry = alloc_image_partition(part_name, len);
311
312 FILE *file = fopen(filename, "rb");
313 if (!file)
314 error(1, errno, "unable to open file `%s'", filename);
315
316 if (fread(entry.data, statbuf.st_size, 1, file) != 1)
317 error(1, errno, "unable to read file `%s'", filename);
318
319 if (add_jffs2_eof) {
320 uint8_t *eof = entry.data + statbuf.st_size, *end = entry.data+entry.size;
321
322 memset(eof, 0xff, end - eof - sizeof(jffs2_eof_mark));
323 memcpy(end - sizeof(jffs2_eof_mark), jffs2_eof_mark, sizeof(jffs2_eof_mark));
324 }
325
326 fclose(file);
327
328 return entry;
329}
330
331
332/**
333 Copies a list of image partitions into an image buffer and generates the image partition table while doing so
334
335 Example image partition table:
336
337 fwup-ptn partition-table base 0x00800 size 0x00800
338 fwup-ptn os-image base 0x01000 size 0x113b45
339 fwup-ptn file-system base 0x114b45 size 0x1d0004
340 fwup-ptn support-list base 0x2e4b49 size 0x000d1
341
342 WA855-RE:
343 fwup-ptn os-image base 0x01000 size 0xd4873
344 fwup-ptn file-system base 0xd5873 size 0x2b8001
345 fwup-ptn partition-table base 0x00800 size 0x00800
346 fwup-ptn soft-version base 0x38d874 size 0x00015
347 fwup-ptn support-list base 0x38d889 size 0x00196
348 fwup-ptn profile base 0x38da1f size 0x000d1
349 fwup-ptn default-config base 0x38daf0 size 0x00429
350
351 Each line of the partition table is terminated with the bytes 09 0d 0a ("\t\r\n"),
352 the end of the partition table is marked with a zero byte.
353
354 The firmware image must contain at least the partition-table and support-list partitions
355 to be accepted. There aren't any alignment constraints for the image partitions.
356
357 The partition-table partition contains the actual flash layout; partitions
358 from the image partition table are mapped to the corresponding flash partitions during
359 the firmware upgrade. The support-list partition contains a list of devices supported by
360 the firmware image.
361
362 The base offsets in the firmware partition table are relative to the end
363 of the vendor information block, so the partition-table partition will
364 actually start at offset 0x1814 of the image.
365
366 I think partition-table must be the first partition in the firmware image.
367*/
368void put_partitions(uint8_t *buffer, const struct image_partition_entry *parts) {
369 size_t i;
370 char *image_pt = (char *)buffer, *end = image_pt + 0x800;
371
372 size_t base = 0x800;
373 for (i = 0; parts[i].name; i++) {
374 memcpy(buffer + base, parts[i].data, parts[i].size);
375
376 size_t len = end-image_pt;
377 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);
378
379 if (w > len-1)
380 error(1, 0, "image partition table overflow?");
381
382 image_pt += w;
383
384 base += parts[i].size;
385 }
386
387 image_pt++;
388
389 memset(image_pt, 0xff, end-image_pt);
390}
391
392/** Generates and writes the image MD5 checksum */
393void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) {
394 MD5_CTX ctx;
395
396 MD5_Init(&ctx);
397 MD5_Update(&ctx, md5_salt, (unsigned int)sizeof(md5_salt));
398 MD5_Update(&ctx, buffer, len);
399 MD5_Final(md5, &ctx);
400}
401
402
403/**
404 Generates the firmware image in factory format
405
406 Image format:
407
408 Bytes (hex) Usage
409 ----------- -----
410 0000-0003 Image size (4 bytes, big endian)
411 0004-0013 MD5 hash (hash of a 16 byte salt and the image data starting with byte 0x14)
412 0014-1013 Vendor information (4096 bytes, padded with 0xff; there seem to be older
413 (VxWorks-based) TP-LINK devices which use a smaller vendor information block)
414 1014-1813 Image partition table (2048 bytes, padded with 0xff)
415 1814-xxxx Firmware partitions
416*/
417void * generate_factory_image(const unsigned char *vendor, size_t vendor_len, const struct image_partition_entry *parts, size_t *len) {
418 *len = 0x1814;
419
420 size_t i;
421 for (i = 0; parts[i].name; i++)
422 *len += parts[i].size;
423
424 uint8_t *image = malloc(*len);
425 if (!image)
426 error(1, errno, "malloc");
427
428 image[0] = *len >> 24;
429 image[1] = *len >> 16;
430 image[2] = *len >> 8;
431 image[3] = *len;
432
433 memcpy(image+0x14, vendor, vendor_len);
434 memset(image+0x14+vendor_len, 0xff, 4096-vendor_len);
435
436 put_partitions(image + 0x1014, parts);
437 put_md5(image+0x04, image+0x14, *len-0x14);
438
439 return image;
440}
441
442/**
443 Generates the firmware image in sysupgrade format
444
445 This makes some assumptions about the provided flash and image partition tables and
446 should be generalized when TP-LINK starts building its safeloader into hardware with
447 different flash layouts.
448*/
449void * generate_sysupgrade_image(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len) {
450 const struct flash_partition_entry *flash_os_image = &flash_parts[5];
451 const struct flash_partition_entry *flash_soft_version = &flash_parts[6];
452 const struct flash_partition_entry *flash_support_list = &flash_parts[7];
453 const struct flash_partition_entry *flash_file_system = &flash_parts[8];
454
455 const struct image_partition_entry *image_os_image = &image_parts[3];
456 const struct image_partition_entry *image_soft_version = &image_parts[1];
457 const struct image_partition_entry *image_support_list = &image_parts[2];
458 const struct image_partition_entry *image_file_system = &image_parts[4];
459
460 assert(strcmp(flash_os_image->name, "os-image") == 0);
461 assert(strcmp(flash_soft_version->name, "soft-version") == 0);
462 assert(strcmp(flash_support_list->name, "support-list") == 0);
463 assert(strcmp(flash_file_system->name, "file-system") == 0);
464
465 assert(strcmp(image_os_image->name, "os-image") == 0);
466 assert(strcmp(image_soft_version->name, "soft-version") == 0);
467 assert(strcmp(image_support_list->name, "support-list") == 0);
468 assert(strcmp(image_file_system->name, "file-system") == 0);
469
470 if (image_os_image->size > flash_os_image->size)
471 error(1, 0, "kernel image too big (more than %u bytes)", (unsigned)flash_os_image->size);
472 if (image_file_system->size > flash_file_system->size)
473 error(1, 0, "rootfs image too big (more than %u bytes)", (unsigned)flash_file_system->size);
474
475 *len = flash_file_system->base - flash_os_image->base + image_file_system->size;
476
477 uint8_t *image = malloc(*len);
478 if (!image)
479 error(1, errno, "malloc");
480
481 memset(image, 0xff, *len);
482
483 memcpy(image, image_os_image->data, image_os_image->size);
484 memcpy(image + flash_soft_version->base - flash_os_image->base, image_soft_version->data, image_soft_version->size);
485 memcpy(image + flash_support_list->base - flash_os_image->base, image_support_list->data, image_support_list->size);
486 memcpy(image + flash_file_system->base - flash_os_image->base, image_file_system->data, image_file_system->size);
487
488 return image;
489}
490
491
492/** Generates an image for CPE210/220/510/520 and writes it to a file */
493static void do_cpe510(const char *output, const char *kernel_image, const char *rootfs_image, uint32_t rev, bool add_jffs2_eof, bool sysupgrade) {
494 struct image_partition_entry parts[6] = {};
495
496 parts[0] = make_partition_table(cpe510_partitions);
497 parts[1] = make_soft_version(rev);
498 parts[2] = make_support_list(cpe510_support_list, sizeof(cpe510_support_list)-1);
499 parts[3] = read_file("os-image", kernel_image, false);
500 parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof);
501
502 size_t len;
503 void *image;
504 if (sysupgrade)
505 image = generate_sysupgrade_image(cpe510_partitions, parts, &len);
506 else
507 image = generate_factory_image(cpe510_vendor, sizeof(cpe510_vendor)-1, parts, &len);
508
509 FILE *file = fopen(output, "wb");
510 if (!file)
511 error(1, errno, "unable to open output file");
512
513 if (fwrite(image, len, 1, file) != 1)
514 error(1, 0, "unable to write output file");
515
516 fclose(file);
517
518 free(image);
519
520 size_t i;
521 for (i = 0; parts[i].name; i++)
522 free_image_partition(parts[i]);
523}
524
525static void do_wa855re(const char *output, const char *kernel_image, const char *rootfs_image, uint32_t rev, bool add_jffs2_eof, bool sysupgrade) {
526 struct image_partition_entry parts[6] = {};
527
528 parts[0] = read_file("os-image", kernel_image, false);
529 parts[1] = read_file("file-system", rootfs_image, add_jffs2_eof);
530 parts[2] = make_partition_table(wa855re_partitions);
531 parts[3] = make_soft_version(rev);
532 parts[4] = make_support_list(wa855re_support_list, sizeof(wa855re_support_list));
533
534 size_t len;
535 void *image;
536 if (sysupgrade)
537 image = generate_sysupgrade_image(wa855re_partitions, parts, &len);
538 else
539 image = generate_factory_image(wa855re_vendor, sizeof(wa855re_vendor)-1, parts, &len);
540
541 FILE *file = fopen(output, "wb");
542 if (!file)
543 error(1, errno, "unable to open output file");
544
545 if (fwrite(image, len, 1, file) != 1)
546 error(1, 0, "unable to write output file");
547
548 fclose(file);
549
550 free(image);
551
552 size_t i;
553 for (i = 0; parts[i].name; i++)
554 free_image_partition(parts[i]);
555}
556
557
558/** Usage output */
559void usage(const char *argv0) {
560 fprintf(stderr,
561 "Usage: %s [OPTIONS...]\n"
562 "\n"
563 "Options:\n"
564 " -B <board> create image for the board specified with <board>\n"
565 " -k <file> read kernel image from the file <file>\n"
566 " -r <file> read rootfs image from the file <file>\n"
567 " -o <file> write output to the file <file>\n"
568 " -V <rev> sets the revision number to <rev>\n"
569 " -j add jffs2 end-of-filesystem markers\n"
570 " -S create sysupgrade instead of factory image\n"
571 " -h show this help\n",
572 argv0
573 );
574};
575
576
577int main(int argc, char *argv[]) {
578 const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL;
579 bool add_jffs2_eof = false, sysupgrade = false;
580 unsigned rev = 0;
581
582 while (true) {
583 int c;
584
585 c = getopt(argc, argv, "B:k:r:o:V:jSh");
586 if (c == -1)
587 break;
588
589 switch (c) {
590 case 'B':
591 board = optarg;
592 break;
593
594 case 'k':
595 kernel_image = optarg;
596 break;
597
598 case 'r':
599 rootfs_image = optarg;
600 break;
601
602 case 'o':
603 output = optarg;
604 break;
605
606 case 'V':
607 sscanf(optarg, "r%u", &rev);
608 break;
609
610 case 'j':
611 add_jffs2_eof = true;
612 break;
613
614 case 'S':
615 sysupgrade = true;
616 break;
617
618 case 'h':
619 usage(argv[0]);
620 return 0;
621
622 default:
623 usage(argv[0]);
624 return 1;
625 }
626 }
627
628 if (!board)
629 error(1, 0, "no board has been specified");
630 if (!kernel_image)
631 error(1, 0, "no kernel image has been specified");
632 if (!rootfs_image)
633 error(1, 0, "no rootfs image has been specified");
634 if (!output)
635 error(1, 0, "no output filename has been specified");
636
637 if (strcmp(board, "CPE510") == 0)
638 do_cpe510(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade);
639 else if (strcmp(board, "WA855RE") == 0)
640 do_wa855re(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade);
641 else
642 error(1, 0, "unsupported board %s", board);
643
644 return 0;
645}