firmware-utils: replace GPL 2.0 boilerplate/reference with SPDX
[openwrt/staging/ldir.git] / tools / firmware-utils / src / mktplinkfw.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
4 *
5 * This tool was based on:
6 * TP-Link WR941 V2 firmware checksum fixing tool.
7 * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn>
8 */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <stdint.h>
13 #include <string.h>
14 #include <unistd.h> /* for unlink() */
15 #include <libgen.h>
16 #include <getopt.h> /* for getopt() */
17 #include <stdarg.h>
18 #include <stdbool.h>
19 #include <endian.h>
20 #include <errno.h>
21 #include <sys/stat.h>
22
23 #include <arpa/inet.h>
24 #include <netinet/in.h>
25
26 #include "md5.h"
27 #include "mktplinkfw-lib.h"
28
29 #define HEADER_VERSION_V1 0x01000000
30 #define HEADER_VERSION_V2 0x02000000
31
32 struct fw_header {
33 uint32_t version; /* header version */
34 char vendor_name[24];
35 char fw_version[36];
36 uint32_t hw_id; /* hardware id */
37 uint32_t hw_rev; /* hardware revision */
38 uint32_t region_code; /* region code */
39 uint8_t md5sum1[MD5SUM_LEN];
40 uint32_t unk2;
41 uint8_t md5sum2[MD5SUM_LEN];
42 uint32_t unk3;
43 uint32_t kernel_la; /* kernel load address */
44 uint32_t kernel_ep; /* kernel entry point */
45 uint32_t fw_length; /* total length of the firmware */
46 uint32_t kernel_ofs; /* kernel data offset */
47 uint32_t kernel_len; /* kernel data length */
48 uint32_t rootfs_ofs; /* rootfs data offset */
49 uint32_t rootfs_len; /* rootfs data length */
50 uint32_t boot_ofs; /* bootloader data offset */
51 uint32_t boot_len; /* bootloader data length */
52 uint16_t ver_hi;
53 uint16_t ver_mid;
54 uint16_t ver_lo;
55 uint8_t pad[130];
56 char region_str1[32];
57 char region_str2[32];
58 uint8_t pad2[160];
59 } __attribute__ ((packed));
60
61 struct fw_region {
62 char name[4];
63 uint32_t code;
64 };
65
66
67 /*
68 * Globals
69 */
70 char *ofname;
71 char *progname;
72 static char *vendor = "TP-LINK Technologies";
73 static char *version = "ver. 1.0";
74 static char *fw_ver = "0.0.0";
75 static uint32_t hdr_ver = HEADER_VERSION_V1;
76
77 static char *layout_id;
78 struct flash_layout *layout;
79 static char *opt_hw_id;
80 static uint32_t hw_id;
81 static char *opt_hw_rev;
82 static uint32_t hw_rev;
83 static uint32_t opt_hdr_ver = 1;
84 static char *country;
85 static const struct fw_region *region;
86 static int fw_ver_lo;
87 static int fw_ver_mid;
88 static int fw_ver_hi;
89 struct file_info kernel_info;
90 static uint32_t kernel_la = 0;
91 static uint32_t kernel_ep = 0;
92 uint32_t kernel_len = 0;
93 struct file_info rootfs_info;
94 uint32_t rootfs_ofs = 0;
95 uint32_t rootfs_align;
96 static struct file_info boot_info;
97 int combined;
98 int strip_padding;
99 int add_jffs2_eof;
100 static uint32_t fw_max_len;
101 static uint32_t reserved_space;
102
103 static struct file_info inspect_info;
104 static int extract = 0;
105 static bool endian_swap = false;
106 static bool rootfs_ofs_calc = false;
107
108 static const char md5salt_normal[MD5SUM_LEN] = {
109 0xdc, 0xd7, 0x3a, 0xa5, 0xc3, 0x95, 0x98, 0xfb,
110 0xdd, 0xf9, 0xe7, 0xf4, 0x0e, 0xae, 0x47, 0x38,
111 };
112
113 static const char md5salt_boot[MD5SUM_LEN] = {
114 0x8c, 0xef, 0x33, 0x5b, 0xd5, 0xc5, 0xce, 0xfa,
115 0xa7, 0x9c, 0x28, 0xda, 0xb2, 0xe9, 0x0f, 0x42,
116 };
117
118 static struct flash_layout layouts[] = {
119 {
120 .id = "4M",
121 .fw_max_len = 0x3c0000,
122 .kernel_la = 0x80060000,
123 .kernel_ep = 0x80060000,
124 .rootfs_ofs = 0x140000,
125 }, {
126 .id = "4Mlzma",
127 .fw_max_len = 0x3c0000,
128 .kernel_la = 0x80060000,
129 .kernel_ep = 0x80060000,
130 .rootfs_ofs = 0x100000,
131 }, {
132 .id = "8M",
133 .fw_max_len = 0x7c0000,
134 .kernel_la = 0x80060000,
135 .kernel_ep = 0x80060000,
136 .rootfs_ofs = 0x140000,
137 }, {
138 .id = "8Mlzma",
139 .fw_max_len = 0x7c0000,
140 .kernel_la = 0x80060000,
141 .kernel_ep = 0x80060000,
142 .rootfs_ofs = 0x100000,
143 }, {
144 .id = "8Mmtk",
145 .fw_max_len = 0x7c0000,
146 .kernel_la = 0x80000000,
147 .kernel_ep = 0x8000c310,
148 .rootfs_ofs = 0x100000,
149 }, {
150 .id = "16M",
151 .fw_max_len = 0xf80000,
152 .kernel_la = 0x80060000,
153 .kernel_ep = 0x80060000,
154 .rootfs_ofs = 0x140000,
155 }, {
156 .id = "16Mlzma",
157 .fw_max_len = 0xf80000,
158 .kernel_la = 0x80060000,
159 .kernel_ep = 0x80060000,
160 .rootfs_ofs = 0x100000,
161 }, {
162 .id = "16Mppc",
163 .fw_max_len = 0xf80000,
164 .kernel_la = 0x00000000 ,
165 .kernel_ep = 0xc0000000,
166 .rootfs_ofs = 0x2a0000,
167 }, {
168 /* terminating entry */
169 }
170 };
171
172 static const struct fw_region regions[] = {
173 /* Default region (universal) uses code 0 as well */
174 {"US", 1},
175 {"EU", 0},
176 {"BR", 0},
177 };
178
179 static const struct fw_region * find_region(const char *country) {
180 size_t i;
181
182 for (i = 0; i < ARRAY_SIZE(regions); i++) {
183 if (strcasecmp(regions[i].name, country) == 0)
184 return &regions[i];
185 }
186
187 return NULL;
188 }
189
190 static void usage(int status)
191 {
192 fprintf(stderr, "Usage: %s [OPTIONS...]\n", progname);
193 fprintf(stderr,
194 "\n"
195 "Options:\n"
196 " -c use combined kernel image\n"
197 " -e swap endianness in kernel load address and entry point\n"
198 " -E <ep> overwrite kernel entry point with <ep> (hexval prefixed with 0x)\n"
199 " -L <la> overwrite kernel load address with <la> (hexval prefixed with 0x)\n"
200 " -H <hwid> use hardware id specified with <hwid>\n"
201 " -W <hwrev> use hardware revision specified with <hwrev>\n"
202 " -C <country> set region code to <country>\n"
203 " -F <id> use flash layout specified with <id>\n"
204 " -k <file> read kernel image from the file <file>\n"
205 " -r <file> read rootfs image from the file <file>\n"
206 " -a <align> align the rootfs start on an <align> bytes boundary\n"
207 " -R <offset> overwrite rootfs offset with <offset> (hexval prefixed with 0x)\n"
208 " -O calculate rootfs offset for combined images\n"
209 " -o <file> write output to the file <file>\n"
210 " -s strip padding from the end of the image\n"
211 " -j add jffs2 end-of-filesystem markers\n"
212 " -N <vendor> set image vendor to <vendor>\n"
213 " -V <version> set image version to <version>\n"
214 " -v <version> set firmware version to <version>\n"
215 " -m <version> set header version to <version>\n"
216 " -i <file> inspect given firmware file <file>\n"
217 " -x extract kernel and rootfs while inspecting (requires -i)\n"
218 " -X <size> reserve <size> bytes in the firmware image (hexval prefixed with 0x)\n"
219 " -h show this screen\n"
220 );
221
222 exit(status);
223 }
224
225 static int check_options(void)
226 {
227 int ret;
228 int exceed_bytes;
229
230 if (inspect_info.file_name) {
231 ret = get_file_stat(&inspect_info);
232 if (ret)
233 return ret;
234
235 return 0;
236 } else if (extract) {
237 ERR("no firmware for inspection specified");
238 return -1;
239 }
240
241 if (opt_hw_id == NULL) {
242 ERR("hardware id not specified");
243 return -1;
244 }
245 hw_id = strtoul(opt_hw_id, NULL, 0);
246
247 if (!combined && layout_id == NULL) {
248 ERR("flash layout is not specified");
249 return -1;
250 }
251
252 if (opt_hw_rev)
253 hw_rev = strtoul(opt_hw_rev, NULL, 0);
254 else
255 hw_rev = 1;
256
257 if (country) {
258 region = find_region(country);
259 if (!region) {
260 ERR("unknown region code \"%s\"", country);
261 return -1;
262 }
263 }
264
265 if (combined) {
266 if (!kernel_la || !kernel_ep) {
267 ERR("kernel loading address and entry point must be specified for combined image");
268 return -1;
269 }
270 } else {
271 layout = find_layout(layouts, layout_id);
272 if (layout == NULL) {
273 ERR("unknown flash layout \"%s\"", layout_id);
274 return -1;
275 }
276
277 if (!kernel_la)
278 kernel_la = layout->kernel_la;
279 if (!kernel_ep)
280 kernel_ep = layout->kernel_ep;
281 if (!rootfs_ofs)
282 rootfs_ofs = layout->rootfs_ofs;
283
284 if (reserved_space > layout->fw_max_len) {
285 ERR("reserved space is not valid");
286 return -1;
287 }
288 }
289
290 if (kernel_info.file_name == NULL) {
291 ERR("no kernel image specified");
292 return -1;
293 }
294
295 ret = get_file_stat(&kernel_info);
296 if (ret)
297 return ret;
298
299 kernel_len = kernel_info.file_size;
300
301 if (!combined) {
302 fw_max_len = layout->fw_max_len - reserved_space;
303
304 if (rootfs_info.file_name == NULL) {
305 ERR("no rootfs image specified");
306 return -1;
307 }
308
309 ret = get_file_stat(&rootfs_info);
310 if (ret)
311 return ret;
312
313 if (rootfs_align) {
314 kernel_len += sizeof(struct fw_header);
315 rootfs_ofs = ALIGN(kernel_len, rootfs_align);
316 kernel_len -= sizeof(struct fw_header);
317
318 DBG("rootfs offset aligned to 0x%u", rootfs_ofs);
319
320 exceed_bytes = kernel_len + rootfs_info.file_size - (fw_max_len - sizeof(struct fw_header));
321 if (exceed_bytes > 0) {
322 ERR("images are too big by %i bytes", exceed_bytes);
323 return -1;
324 }
325 } else {
326 exceed_bytes = kernel_info.file_size - (rootfs_ofs - sizeof(struct fw_header));
327 if (exceed_bytes > 0) {
328 ERR("kernel image is too big by %i bytes", exceed_bytes);
329 return -1;
330 }
331
332 exceed_bytes = rootfs_info.file_size - (fw_max_len - rootfs_ofs);
333 if (exceed_bytes > 0) {
334 ERR("rootfs image is too big by %i bytes", exceed_bytes);
335 return -1;
336 }
337 }
338 }
339
340 if (ofname == NULL) {
341 ERR("no output file specified");
342 return -1;
343 }
344
345 ret = sscanf(fw_ver, "%d.%d.%d", &fw_ver_hi, &fw_ver_mid, &fw_ver_lo);
346 if (ret != 3) {
347 ERR("invalid firmware version '%s'", fw_ver);
348 return -1;
349 }
350
351 if (opt_hdr_ver == 1) {
352 hdr_ver = HEADER_VERSION_V1;
353 } else if (opt_hdr_ver == 2) {
354 hdr_ver = HEADER_VERSION_V2;
355 } else {
356 ERR("invalid header version '%u'", opt_hdr_ver);
357 return -1;
358 }
359
360 return 0;
361 }
362
363 void fill_header(char *buf, int len)
364 {
365 struct fw_header *hdr = (struct fw_header *)buf;
366
367 memset(hdr, 0, sizeof(struct fw_header));
368
369 hdr->version = htonl(hdr_ver);
370 strncpy(hdr->vendor_name, vendor, sizeof(hdr->vendor_name));
371 strncpy(hdr->fw_version, version, sizeof(hdr->fw_version));
372 hdr->hw_id = htonl(hw_id);
373 hdr->hw_rev = htonl(hw_rev);
374
375 hdr->kernel_la = htonl(kernel_la);
376 hdr->kernel_ep = htonl(kernel_ep);
377 hdr->kernel_ofs = htonl(sizeof(struct fw_header));
378 hdr->kernel_len = htonl(kernel_len);
379
380 if (!combined) {
381 if (boot_info.file_size == 0)
382 memcpy(hdr->md5sum1, md5salt_normal, sizeof(hdr->md5sum1));
383 else
384 memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1));
385
386 hdr->fw_length = htonl(layout->fw_max_len);
387 hdr->rootfs_ofs = htonl(rootfs_ofs);
388 hdr->rootfs_len = htonl(rootfs_info.file_size);
389 }
390
391 if (combined && rootfs_ofs_calc) {
392 hdr->rootfs_ofs = htonl(sizeof(struct fw_header) + kernel_len);
393 }
394
395 hdr->ver_hi = htons(fw_ver_hi);
396 hdr->ver_mid = htons(fw_ver_mid);
397 hdr->ver_lo = htons(fw_ver_lo);
398
399 if (region) {
400 hdr->region_code = htonl(region->code);
401 snprintf(
402 hdr->region_str1, sizeof(hdr->region_str1), "00000000;%02X%02X%02X%02X;",
403 region->name[0], region->name[1], region->name[2], region->name[3]
404 );
405 snprintf(
406 hdr->region_str2, sizeof(hdr->region_str2), "%02X%02X%02X%02X",
407 region->name[0], region->name[1], region->name[2], region->name[3]
408 );
409 }
410
411 if (endian_swap) {
412 hdr->kernel_la = bswap_32(hdr->kernel_la);
413 hdr->kernel_ep = bswap_32(hdr->kernel_ep);
414 }
415
416 if (!combined)
417 get_md5(buf, len, hdr->md5sum1);
418 }
419
420 static int inspect_fw(void)
421 {
422 char *buf;
423 struct fw_header *hdr;
424 uint8_t md5sum[MD5SUM_LEN];
425 int ret = EXIT_FAILURE;
426
427 buf = malloc(inspect_info.file_size);
428 if (!buf) {
429 ERR("no memory for buffer!\n");
430 goto out;
431 }
432
433 ret = read_to_buf(&inspect_info, buf);
434 if (ret)
435 goto out_free_buf;
436 hdr = (struct fw_header *)buf;
437
438 inspect_fw_pstr("File name", inspect_info.file_name);
439 inspect_fw_phexdec("File size", inspect_info.file_size);
440
441 if ((ntohl(hdr->version) != HEADER_VERSION_V1) &&
442 (ntohl(hdr->version) != HEADER_VERSION_V2)) {
443 ERR("file does not seem to have V1/V2 header!\n");
444 goto out_free_buf;
445 }
446
447 inspect_fw_phexdec("Version 1 Header size", sizeof(struct fw_header));
448
449 memcpy(md5sum, hdr->md5sum1, sizeof(md5sum));
450 if (ntohl(hdr->boot_len) == 0)
451 memcpy(hdr->md5sum1, md5salt_normal, sizeof(md5sum));
452 else
453 memcpy(hdr->md5sum1, md5salt_boot, sizeof(md5sum));
454 get_md5(buf, inspect_info.file_size, hdr->md5sum1);
455
456 if (memcmp(md5sum, hdr->md5sum1, sizeof(md5sum))) {
457 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(*ERROR*)");
458 inspect_fw_pmd5sum(" --> expected", hdr->md5sum1, "");
459 } else {
460 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(ok)");
461 }
462 if (ntohl(hdr->unk2) != 0)
463 inspect_fw_phexdec("Unknown value 2", hdr->unk2);
464 inspect_fw_pmd5sum("Header MD5Sum2", hdr->md5sum2,
465 "(purpose yet unknown, unchecked here)");
466 if (ntohl(hdr->unk3) != 0)
467 inspect_fw_phexdec("Unknown value 3", hdr->unk3);
468
469 printf("\n");
470
471 inspect_fw_pstr("Vendor name", hdr->vendor_name);
472 inspect_fw_pstr("Firmware version", hdr->fw_version);
473 inspect_fw_phex("Hardware ID", ntohl(hdr->hw_id));
474 inspect_fw_phex("Hardware Revision", ntohl(hdr->hw_rev));
475 inspect_fw_phex("Region code", ntohl(hdr->region_code));
476
477 printf("\n");
478
479 inspect_fw_phexdec("Kernel data offset",
480 ntohl(hdr->kernel_ofs));
481 inspect_fw_phexdec("Kernel data length",
482 ntohl(hdr->kernel_len));
483 inspect_fw_phex("Kernel load address",
484 ntohl(hdr->kernel_la));
485 inspect_fw_phex("Kernel entry point",
486 ntohl(hdr->kernel_ep));
487 inspect_fw_phexdec("Rootfs data offset",
488 ntohl(hdr->rootfs_ofs));
489 inspect_fw_phexdec("Rootfs data length",
490 ntohl(hdr->rootfs_len));
491 inspect_fw_phexdec("Boot loader data offset",
492 ntohl(hdr->boot_ofs));
493 inspect_fw_phexdec("Boot loader data length",
494 ntohl(hdr->boot_len));
495 inspect_fw_phexdec("Total firmware length",
496 ntohl(hdr->fw_length));
497
498 if (extract) {
499 FILE *fp;
500 char *filename;
501
502 printf("\n");
503
504 filename = malloc(strlen(inspect_info.file_name) + 8);
505 sprintf(filename, "%s-kernel", inspect_info.file_name);
506 printf("Extracting kernel to \"%s\"...\n", filename);
507 fp = fopen(filename, "w");
508 if (fp) {
509 if (!fwrite(buf + ntohl(hdr->kernel_ofs),
510 ntohl(hdr->kernel_len), 1, fp)) {
511 ERR("error in fwrite(): %s", strerror(errno));
512 }
513 fclose(fp);
514 } else {
515 ERR("error in fopen(): %s", strerror(errno));
516 }
517 free(filename);
518
519 filename = malloc(strlen(inspect_info.file_name) + 8);
520 sprintf(filename, "%s-rootfs", inspect_info.file_name);
521 printf("Extracting rootfs to \"%s\"...\n", filename);
522 fp = fopen(filename, "w");
523 if (fp) {
524 if (!fwrite(buf + ntohl(hdr->rootfs_ofs),
525 ntohl(hdr->rootfs_len), 1, fp)) {
526 ERR("error in fwrite(): %s", strerror(errno));
527 }
528 fclose(fp);
529 } else {
530 ERR("error in fopen(): %s", strerror(errno));
531 }
532 free(filename);
533 }
534
535 out_free_buf:
536 free(buf);
537 out:
538 return ret;
539 }
540
541 int main(int argc, char *argv[])
542 {
543 int ret = EXIT_FAILURE;
544
545 progname = basename(argv[0]);
546
547 while ( 1 ) {
548 int c;
549
550 c = getopt(argc, argv, "a:H:E:F:L:m:V:N:W:C:ci:k:r:R:o:OxX:ehsjv:");
551 if (c == -1)
552 break;
553
554 switch (c) {
555 case 'a':
556 sscanf(optarg, "0x%x", &rootfs_align);
557 break;
558 case 'H':
559 opt_hw_id = optarg;
560 break;
561 case 'E':
562 sscanf(optarg, "0x%x", &kernel_ep);
563 break;
564 case 'F':
565 layout_id = optarg;
566 break;
567 case 'W':
568 opt_hw_rev = optarg;
569 break;
570 case 'C':
571 country = optarg;
572 break;
573 case 'L':
574 sscanf(optarg, "0x%x", &kernel_la);
575 break;
576 case 'm':
577 sscanf(optarg, "%u", &opt_hdr_ver);
578 break;
579 case 'V':
580 version = optarg;
581 break;
582 case 'v':
583 fw_ver = optarg;
584 break;
585 case 'N':
586 vendor = optarg;
587 break;
588 case 'c':
589 combined++;
590 break;
591 case 'k':
592 kernel_info.file_name = optarg;
593 break;
594 case 'r':
595 rootfs_info.file_name = optarg;
596 break;
597 case 'R':
598 sscanf(optarg, "0x%x", &rootfs_ofs);
599 break;
600 case 'o':
601 ofname = optarg;
602 break;
603 case 'O':
604 rootfs_ofs_calc = 1;
605 break;
606 case 's':
607 strip_padding = 1;
608 break;
609 case 'i':
610 inspect_info.file_name = optarg;
611 break;
612 case 'j':
613 add_jffs2_eof = 1;
614 break;
615 case 'x':
616 extract = 1;
617 break;
618 case 'e':
619 endian_swap = true;
620 break;
621 case 'h':
622 usage(EXIT_SUCCESS);
623 break;
624 case 'X':
625 sscanf(optarg, "0x%x", &reserved_space);
626 break;
627 default:
628 usage(EXIT_FAILURE);
629 break;
630 }
631 }
632
633 ret = check_options();
634 if (ret)
635 goto out;
636
637 if (!inspect_info.file_name)
638 ret = build_fw(sizeof(struct fw_header));
639 else
640 ret = inspect_fw();
641
642 out:
643 return ret;
644 }