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