tplink-safeloader: support Archer C6v3.0 (BR)
[project/firmware-utils.git] / src / jcgimage.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * jcgimage - Create a JCG firmware image
4 *
5 * Copyright (C) 2015 Reinhard Max <reinhard@m4x.de>
6 * Copyright (C) 2019 Davide Fioravanti <pantanastyle@gmail.com>
7 */
8
9 /*
10 * JCG firmware update images consist of a 512 byte header and a
11 * modified uImage (details below) as the payload.
12 *
13 * The payload is obfuscated by XORing it with a key that is generated
14 * from parts of the header. Fortunately only non-essential parts of
15 * the header are used for this and zeroing them results in a zero
16 * key, effectively disabling the obfuscation and allowing us to use
17 * clear text payloads.
18 *
19 * The mandatory parts of the header are:
20 *
21 * - A magic string of "YSZJ" at offset 0.
22 * - A value of 1 at offset 39 (header format version?)
23 * - A CRC32 checksum of the payload at offset 504.
24 * - A CRC32 checksum of the header at offset 508.
25 *
26 * An image constructed by these rules will be accepted by JCG's
27 * U-Boot in resuce mode via TFTP and the payload will be written to
28 * the flash starting at offset 0x00050000.
29 *
30 * JCG's U-Boot does check the content or size of the payload
31 * image. If it is too large, it wraps around and overwrites U-Boot,
32 * requiring JTAG to revive the board. To prevent such bricking from
33 * happening, this tool refuses to build such overlong images.
34 *
35 * Using -m is possible to set the maximum size of the payload.
36 * Otherwise the default MAXSIZE will be used.
37 * For an 8Mb flash, the corresponding maxsize is:
38 * 8 * 1024 * 1024 - 5 * 64 * 1024 = 8388608 - 327680 = 8060928
39 *
40 * Two more conditions have to be met for a JCG image to be accepted
41 * as a valid update by the web interface of the stock firware:
42 *
43 * - The bytes at offsets 109 and 111 in the header must be a binary
44 * representation of the first two components of the firmware
45 * version as displayed in the update web form, or it will be
46 * rejected as "incorrect product".
47 *
48 * - The payload must start with a valid uImage header whose data
49 * CRC checksum matches the whole rest of the update file rather
50 * than just the number of bytes specified in the size field of the
51 * header.
52 *
53 * This last condition is met by JCG's original firmware images,
54 * because they have both, kernel and rootfs inside the uImage and
55 * abuse the last four bytes of the name field to record the offset of
56 * the file system from the start of the uImage header. This tool
57 * produces such images when called with -k and -r, which are meant to
58 * repack the original firmware after modifying the file systen,
59 * e.g. to add debugging tools and enable shell access.
60 *
61 * In contrast, OpenWrt sysupgrade images consist of a uImage that
62 * only contains the kernel and has the rootfs appended to it. Hence,
63 * the CRC over kernel and file system does not match the one in the
64 * uImage header. Fixing this by adjusting the uImage header is not
65 * possible, because it makes the uImage unusable for booting. Instead
66 * we append four "patch" bytes to the end of the file system, that
67 * are calculated to force the checksum of kernel+fs to be the same as
68 * for the kernel alone.
69 *
70 */
71
72 #include <zlib.h>
73 #include <stdio.h>
74 #include <string.h>
75 #include <sys/types.h>
76 #include <sys/stat.h>
77 #include <fcntl.h>
78 #include <unistd.h>
79 #include <libgen.h>
80 #include <stdlib.h>
81 #include <errno.h>
82 #include <err.h>
83 #include <time.h>
84 #include <sys/mman.h>
85 #include <arpa/inet.h>
86 #include <assert.h>
87 #include <inttypes.h>
88
89 /*
90 * JCG Firmware image header
91 */
92 #define JH_MAGIC 0x59535a4a /* "YSZJ" */
93 struct jcg_header {
94 uint32_t jh_magic;
95 uint8_t jh_version[32]; /* Firmware version string.
96 Fill with zeros to avoid encryption */
97 uint32_t jh_type; /* must be 1 */
98 uint8_t jh_info[64]; /* Firmware info string. Fill with
99 zeros to avoid encryption */
100 uint32_t jh_time; /* Image creation time in seconds since
101 * the Epoch. Does not seem to be used
102 * by the stock firmware. */
103 uint16_t jh_major; /* Major fimware version */
104 uint16_t jh_minor; /* Minor fimrmware version */
105 uint8_t jh_unknown[392]; /* Apparently unused and all zeros */
106 uint32_t jh_dcrc; /* CRC checksum of the payload */
107 uint32_t jh_hcrc; /* CRC checksum of the header */
108 };
109
110 /*
111 * JCG uses a modified uImage header that replaces the last four bytes
112 * of the image name with the length of the kernel in the image.
113 */
114 #define IH_MAGIC 0x27051956 /* Image Magic Number */
115 #define IH_NMLEN 28 /* Image Name Length */
116
117 struct uimage_header {
118 uint32_t ih_magic; /* Image Header Magic Number */
119 uint32_t ih_hcrc; /* Image Header CRC Checksum */
120 uint32_t ih_time; /* Image Creation Timestamp */
121 uint32_t ih_size; /* Image Data Size */
122 uint32_t ih_load; /* Data Load Address */
123 uint32_t ih_ep; /* Entry Point Address */
124 uint32_t ih_dcrc; /* Image Data CRC Checksum */
125 uint8_t ih_os; /* Operating System */
126 uint8_t ih_arch; /* CPU architecture */
127 uint8_t ih_type; /* Image Type */
128 uint8_t ih_comp; /* Compression Type */
129 uint8_t ih_name[IH_NMLEN];/* Image Name */
130 uint32_t ih_fsoff; /* Offset of the file system
131 partition from the start of
132 the header */
133 };
134
135 /*
136 * Open the named file and return its size and file descriptor.
137 * Exit in case of errors.
138 */
139 int
140 opensize(char *name, size_t *size)
141 {
142 struct stat s;
143 int fd = open(name, O_RDONLY);
144 if (fd < 0)
145 err(1, "cannot open \"%s\"", name);
146
147 if (fstat(fd, &s) == -1)
148 err(1, "cannot stat \"%s\"", name);
149
150 *size = s.st_size;
151 return fd;
152 }
153
154 static time_t source_date_epoch = -1;
155 static void set_source_date_epoch() {
156 char *env = getenv("SOURCE_DATE_EPOCH");
157 char *endptr = env;
158 errno = 0;
159 if (env && *env) {
160 source_date_epoch = strtoull(env, &endptr, 10);
161 if (errno || (endptr && *endptr != '\0')) {
162 fprintf(stderr, "Invalid SOURCE_DATE_EPOCH");
163 exit(1);
164 }
165 }
166 }
167
168 /*
169 * Write the JCG header
170 */
171 void
172 mkjcgheader(struct jcg_header *h, size_t psize, char *version)
173 {
174 uLong crc;
175 uint16_t major = 0, minor = 0;
176 void *payload = (void *)h + sizeof(*h);
177 time_t t;
178
179 if (source_date_epoch != -1)
180 t = source_date_epoch;
181 else if ((time(&t) == (time_t)(-1)))
182 err(1, "time call failed");
183
184
185 if (version != NULL)
186 if (sscanf(version, "%hu.%hu", &major, &minor) != 2)
187 err(1, "cannot parse version \"%s\"", version);
188
189 memset(h, 0, sizeof(*h));
190 h->jh_magic = htonl(JH_MAGIC);
191 h->jh_type = htonl(1);
192 h->jh_time = htonl(t);
193 h->jh_major = htons(major);
194 h->jh_minor = htons(minor);
195
196 /* CRC over JCG payload (uImage) */
197 crc = crc32(0L, Z_NULL, 0);
198 crc = crc32(crc, payload, psize);
199 h->jh_dcrc = htonl(crc);
200
201 /* CRC over JCG header */
202 crc = crc32(0L, Z_NULL, 0);
203 crc = crc32(crc, (void *)h, sizeof(*h));
204 h->jh_hcrc = htonl(crc);
205 }
206
207 /*
208 * Write the uImage header
209 */
210 void
211 mkuheader(struct uimage_header *h, size_t ksize, size_t fsize)
212 {
213 uLong crc;
214 void *payload = (void *)h + sizeof(*h);
215
216 // printf("mkuheader: %p, %zd, %zd\n", h, ksize, fsize);
217 memset(h, 0, sizeof(*h));
218 h->ih_magic = htonl(IH_MAGIC);
219 h->ih_time = htonl(time(NULL));
220 h->ih_size = htonl(ksize + fsize);
221 h->ih_load = htonl(0x80000000);
222 h->ih_ep = htonl(0x80292000);
223 h->ih_os = 0x05;
224 h->ih_arch = 0x05;
225 h->ih_type = 0x02;
226 h->ih_comp = 0x03;
227 h->ih_fsoff = htonl(sizeof(*h) + ksize);
228 strcpy((char *)h->ih_name, "Linux Kernel Image");
229
230 /* CRC over uImage payload (kernel and file system) */
231 crc = crc32(0L, Z_NULL, 0);
232 crc = crc32(crc, payload, ntohl(h->ih_size));
233 h->ih_dcrc = htonl(crc);
234 printf("CRC1: %08lx\n", crc);
235
236 /* CRC over uImage header */
237 crc = crc32(0L, Z_NULL, 0);
238 crc = crc32(crc, (void *)h, sizeof(*h));
239 h->ih_hcrc = htonl(crc);
240 printf("CRC2: %08lx\n", crc);
241 }
242
243 /*
244 * Calculate a "patch" value and write it into the last four bytes of
245 * buf, so that the CRC32 checksum of the whole buffer is dcrc.
246 *
247 * Based on: SAR-PR-2006-05: Reversing CRC – Theory and Practice.
248 * Martin Stigge, Henryk Plötz, Wolf Müller, Jens-Peter Redlich.
249 * http://sar.informatik.hu-berlin.de/research/publications/#SAR-PR-2006-05
250 */
251 void
252 craftcrc(uint32_t dcrc, uint8_t *buf, size_t len)
253 {
254 int i;
255 uint32_t a;
256 uint32_t patch = 0;
257 uint32_t crc = crc32(0L, Z_NULL, 0);
258
259 a = ~dcrc;
260 for (i = 0; i < 32; i++) {
261 if (patch & 1)
262 patch = (patch >> 1) ^ 0xedb88320L;
263 else
264 patch >>= 1;
265
266 if (a & 1)
267 patch ^= 0x5b358fd3L;
268
269 a >>= 1;
270 }
271 patch ^= ~crc32(crc, buf, len - 4);
272 for (i = 0; i < 4; i++) {
273 buf[len - 4 + i] = patch & 0xff;
274 patch >>= 8;
275 }
276 /* Verify that we actually get the desired result */
277 crc = crc32(0L, Z_NULL, 0);
278 crc = crc32(crc, buf, len);
279 if (crc != dcrc)
280 errx(1, "CRC patching is broken: wanted %08x, but got %08x.",
281 dcrc, crc);
282
283 }
284
285 void
286 usage() {
287 fprintf(stderr, "Usage:\n"
288 "jcgimage -o outfile -u uImage [-m maxsize] [-v version]\n"
289 "jcgimage -o outfile -k kernel -f rootfs [-m maxsize] [-v version]\n");
290 exit(1);
291 }
292
293 #define MODE_UNKNOWN 0
294 #define MODE_UIMAGE 1
295 #define MODE_KR 2
296
297 /* The output image must not be larger than 4MiB - 5*64kiB */
298 #define MAXSIZE (size_t)(4 * 1024 * 1024 - 5 * 64 * 1024)
299
300 int
301 main(int argc, char **argv)
302 {
303 struct jcg_header *jh;
304 struct uimage_header *uh;
305 int c;
306 char *imagefile = NULL;
307 char *file1 = NULL;
308 char *file2 = NULL;
309 char *version = NULL;
310 size_t maxsize = MAXSIZE;
311 char *endptr;
312 int mode = MODE_UNKNOWN;
313 int fdo, fd1, fd2;
314 size_t size1, size2, sizeu, sizeo, off1, off2;
315 void *map;
316
317 /* Make sure the headers have the right size */
318 assert(sizeof(struct jcg_header) == 512);
319 assert(sizeof(struct uimage_header) == 64);
320 set_source_date_epoch();
321
322 while ((c = getopt(argc, argv, "o:k:f:u:v:m:h")) != -1) {
323 switch (c) {
324 case 'o':
325 imagefile = optarg;
326 break;
327 case 'k':
328 if (mode == MODE_UIMAGE)
329 errx(1,"-k cannot be combined with -u");
330
331 mode = MODE_KR;
332 file1 = optarg;
333 break;
334 case 'f':
335 if (mode == MODE_UIMAGE)
336 errx(1,"-f cannot be combined with -u");
337
338 mode = MODE_KR;
339 file2 = optarg;
340 break;
341 case 'u':
342 if (mode == MODE_KR)
343 errx(1,"-u cannot be combined with -k and -r");
344
345 mode = MODE_UIMAGE;
346 file1 = optarg;
347 break;
348 case 'm':
349 if (optarg != NULL)
350 maxsize = strtoimax(optarg, &endptr, 10);
351
352 break;
353 case 'v':
354 version = optarg;
355 break;
356 case 'h':
357 default:
358 usage();
359 }
360 }
361 if (optind != argc)
362 errx(1, "illegal arg \"%s\"", argv[optind]);
363
364 if (imagefile == NULL)
365 errx(1, "no output file specified");
366
367 if (mode == MODE_UNKNOWN)
368 errx(1, "specify either -u or -k and -r");
369
370 if (mode == MODE_KR) {
371 if (file1 == NULL || file2 == NULL)
372 errx(1, "need -k and -r");
373
374 fd2 = opensize(file2, &size2);
375 }
376 fd1 = opensize(file1, &size1);
377 if (mode == MODE_UIMAGE) {
378 off1 = sizeof(*jh);
379 sizeu = size1 + 4;
380 sizeo = sizeof(*jh) + sizeu;
381 } else {
382 off1 = sizeof(*jh) + sizeof(*uh);
383 off2 = sizeof(*jh) + sizeof(*uh) + size1;
384 sizeu = sizeof(*uh) + size1 + size2;
385 sizeo = sizeof(*jh) + sizeu;
386 }
387
388 if (sizeo > maxsize)
389 errx(1, "payload too large: %zd > %zd\n", sizeo, maxsize);
390
391
392 fdo = open(imagefile, O_RDWR | O_CREAT | O_TRUNC, 00644);
393 if (fdo < 0)
394 err(1, "cannot open \"%s\"", imagefile);
395
396
397 if (ftruncate(fdo, sizeo) == -1)
398 err(1, "cannot grow \"%s\" to %zd bytes", imagefile, sizeo);
399
400 map = mmap(NULL, sizeo, PROT_READ|PROT_WRITE, MAP_SHARED, fdo, 0);
401 uh = map + sizeof(*jh);
402 if (map == MAP_FAILED)
403 err(1, "cannot mmap \"%s\"", imagefile);
404
405
406 if (read(fd1, map + off1, size1) != size1)
407 err(1, "cannot copy %s", file1);
408
409
410 if (mode == MODE_KR) {
411 if (read(fd2, map+off2, size2) != size2)
412 err(1, "cannot copy %s", file2);
413
414 mkuheader(uh, size1, size2);
415 } else if (mode == MODE_UIMAGE)
416 craftcrc(ntohl(uh->ih_dcrc), (void*)uh + sizeof(*uh),
417 sizeu - sizeof(*uh));
418
419 mkjcgheader(map, sizeu, version);
420 munmap(map, sizeo);
421 close(fdo);
422 return 0;
423 }