2 Copyright (c) 2014, Matthias Schiffer <mschiffer@universe-factory.net>
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
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.
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.
30 Image generation tool for the TP-LINK SafeLoader as seen on
31 TP-LINK Pharos devices (CPE210/220/510/520)
45 #include <arpa/inet.h>
47 #include <sys/types.h>
53 #define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); })
56 /** An image partition table entry */
57 struct image_partition_entry {
63 /** A flash partition table entry */
64 struct flash_partition_entry {
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);
77 /** The content of the soft-version structure */
78 struct __attribute__((__packed__)) soft_version {
82 uint8_t version_major;
83 uint8_t version_minor;
84 uint8_t version_patch;
94 static const uint8_t jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde};
100 Fortunately, TP-LINK seems to use the same salt for most devices which use
101 the new image format.
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,
111 /** Vendor information for CPE210/220/510/520 */
112 static const char cpe510_vendor[] = "CPE510(TP-LINK|UN|N300-5):1.0\r\n";
114 /** Vendor information for C2600 */
115 static const char c2600_vendor[] = "";
117 /** Vendor information for EAP120 */
118 static const char eap120_vendor[] = "EAP120(TP-LINK|UN|N300-2):1.0\r\n";
121 The flash partition table for CPE210/220/510/520;
122 it is the same as the one used by the stock images.
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},
142 The flash partition table for C2600;
143 it is the same as the one used by the stock images.
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},
174 /** The flash partition table for EAP120;
175 it is the same as the one used by the stock images.
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},
194 The support list for CPE210/220
196 static const char cpe210_support_list[] =
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";
203 The support list for CPE210/220/510/520
205 static const char cpe510_support_list[] =
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";
213 The support list for C2600
215 static const char c2600_support_list[] =
217 "{product_name:Archer C2600,product_ver:1.0.0,special_id:00000000}\r\n";
220 The support list for EAP120
222 static const char eap120_support_list[] =
224 "EAP120(TP-LINK|UN|N300-2):1.0\r\n";
226 #define error(_ret, _errno, _str, ...) \
228 fprintf(stderr, _str ": %s\n", ## __VA_ARGS__, \
235 /** Stores a uint32 as big endian */
236 static inline void put32(uint8_t *buf, uint32_t val) {
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)};
247 error(1, errno, "malloc");
252 /** Frees an image partition */
253 static void free_image_partition(struct image_partition_entry entry) {
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);
261 char *s = (char *)entry.data, *end = (char *)(s+entry.size);
269 for (i = 0; p[i].name; i++) {
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);
274 error(1, 0, "flash partition table overflow?");
281 memset(s, 0xff, end-s);
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;
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;
300 if (time(&t) == (time_t)(-1))
301 error(1, errno, "time");
303 struct tm *tm = localtime(&t);
305 s->magic = htonl(0x0000000c);
309 s->version_major = 0;
310 s->version_minor = 0;
311 s->version_patch = 0;
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);
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);
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';
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) {
341 if (stat(filename, &statbuf) < 0)
342 error(1, errno, "unable to stat file `%s'", filename);
344 size_t len = statbuf.st_size;
347 len = ALIGN(len, 0x10000) + sizeof(jffs2_eof_mark);
349 struct image_partition_entry entry = alloc_image_partition(part_name, len);
351 FILE *file = fopen(filename, "rb");
353 error(1, errno, "unable to open file `%s'", filename);
355 if (fread(entry.data, statbuf.st_size, 1, file) != 1)
356 error(1, errno, "unable to read file `%s'", filename);
359 uint8_t *eof = entry.data + statbuf.st_size, *end = entry.data+entry.size;
361 memset(eof, 0xff, end - eof - sizeof(jffs2_eof_mark));
362 memcpy(end - sizeof(jffs2_eof_mark), jffs2_eof_mark, sizeof(jffs2_eof_mark));
372 Copies a list of image partitions into an image buffer and generates the image partition table while doing so
374 Example image partition table:
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
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.
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.
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
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.
396 I think partition-table must be the first partition in the firmware image.
398 static void put_partitions(uint8_t *buffer, const struct image_partition_entry *parts) {
400 char *image_pt = (char *)buffer, *end = image_pt + 0x800;
403 for (i = 0; parts[i].name; i++) {
404 memcpy(buffer + base, parts[i].data, parts[i].size);
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);
410 error(1, 0, "image partition table overflow?");
414 base += parts[i].size;
419 memset(image_pt, 0xff, end-image_pt);
422 /** Generates and writes the image MD5 checksum */
423 static void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) {
427 MD5_Update(&ctx, md5_salt, (unsigned int)sizeof(md5_salt));
428 MD5_Update(&ctx, buffer, len);
429 MD5_Final(md5, &ctx);
434 Generates the firmware image in factory format
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
448 static void * generate_factory_image(const char *vendor, const struct image_partition_entry *parts, size_t *len) {
452 for (i = 0; parts[i].name; i++)
453 *len += parts[i].size;
455 uint8_t *image = malloc(*len);
457 error(1, errno, "malloc");
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);
466 put_partitions(image + 0x1014, parts);
467 put_md5(image+0x04, image+0x14, *len-0x14);
473 Generates the firmware image in sysupgrade format
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.
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];
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];
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);
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);
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);
505 *len = flash_file_system->base - flash_os_image->base + image_file_system->size;
507 uint8_t *image = malloc(*len);
509 error(1, errno, "malloc");
511 memset(image, 0xff, *len);
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);
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];
525 const struct image_partition_entry *image_os_image = &image_parts[3];
526 const struct image_partition_entry *image_file_system = &image_parts[4];
528 assert(strcmp(flash_os_image->name, "os-image") == 0);
529 assert(strcmp(flash_file_system->name, "file-system") == 0);
531 assert(strcmp(image_os_image->name, "os-image") == 0);
532 assert(strcmp(image_file_system->name, "file-system") == 0);
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);
539 *len = flash_file_system->base - flash_os_image->base + image_file_system->size;
541 uint8_t *image = malloc(*len);
543 error(1, errno, "malloc");
545 memset(image, 0xff, *len);
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);
552 static void *generate_sysupgrade_image_eap120(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len)
554 const struct flash_partition_entry *flash_os_image = &flash_parts[6];
555 const struct flash_partition_entry *flash_file_system = &flash_parts[7];
557 const struct image_partition_entry *image_os_image = &image_parts[3];
558 const struct image_partition_entry *image_file_system = &image_parts[4];
560 assert(strcmp(flash_os_image->name, "os-image") == 0);
561 assert(strcmp(flash_file_system->name, "file-system") == 0);
563 assert(strcmp(image_os_image->name, "os-image") == 0);
564 assert(strcmp(image_file_system->name, "file-system") == 0);
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);
571 *len = flash_file_system->base - flash_os_image->base + image_file_system->size;
573 uint8_t *image = malloc(*len);
575 error(1, errno, "malloc");
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);
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,
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,
598 const char *support_list) {
599 struct image_partition_entry parts[6] = {};
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);
610 image = generate_sysupgrade_image(cpe510_partitions, parts, &len);
612 image = generate_factory_image(cpe510_vendor, parts, &len);
614 FILE *file = fopen(output, "wb");
616 error(1, errno, "unable to open output file");
618 if (fwrite(image, len, 1, file) != 1)
619 error(1, 0, "unable to write output file");
626 for (i = 0; parts[i].name; i++)
627 free_image_partition(parts[i]);
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] = {};
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);
643 image = generate_sysupgrade_image_c2600(c2600_partitions, parts, &len);
645 image = generate_factory_image(c2600_vendor, parts, &len);
647 FILE *file = fopen(output, "wb");
649 error(1, errno, "unable to open output file");
651 if (fwrite(image, len, 1, file) != 1)
652 error(1, 0, "unable to write output file");
659 for (i = 0; parts[i].name; i++)
660 free_image_partition(parts[i]);
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,
671 struct device_info *info) {
672 struct image_partition_entry parts[6] = {};
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);
683 image = info->generate_sysupgrade_image(info->partitions, parts, &len);
685 image = generate_factory_image(info->vendor, parts, &len);
687 FILE *file = fopen(output, "wb");
689 error(1, errno, "unable to open output file");
691 if (fwrite(image, len, 1, file) != 1)
692 error(1, 0, "unable to write output file");
699 for (i = 0; parts[i].name; i++)
700 free_image_partition(parts[i]);
704 static void usage(const char *argv0) {
706 "Usage: %s [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",
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;
730 c = getopt(argc, argv, "B:k:r:o:V:jSh");
740 kernel_image = optarg;
744 rootfs_image = optarg;
752 sscanf(optarg, "r%u", &rev);
756 add_jffs2_eof = true;
774 error(1, 0, "no board has been specified");
776 error(1, 0, "no kernel image has been specified");
778 error(1, 0, "no rootfs image has been specified");
780 error(1, 0, "no output filename has been specified");
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);
791 error(1, 0, "unsupported board %s", board);