tools: otrx: allow own magic
[project/firmware-utils.git] / src / otrx.c
1 /*
2 * otrx
3 *
4 * Copyright (C) 2015-2017 Rafał Miłecki <zajec5@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 */
11
12 #include <byteswap.h>
13 #include <endian.h>
14 #include <errno.h>
15 #include <stdint.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20
21 #if !defined(__BYTE_ORDER)
22 #error "Unknown byte order"
23 #endif
24
25 #if __BYTE_ORDER == __BIG_ENDIAN
26 #define cpu_to_le32(x) bswap_32(x)
27 #define le32_to_cpu(x) bswap_32(x)
28 #elif __BYTE_ORDER == __LITTLE_ENDIAN
29 #define cpu_to_le32(x) (x)
30 #define le32_to_cpu(x) (x)
31 #else
32 #error "Unsupported endianness"
33 #endif
34
35 #define TRX_MAGIC 0x30524448
36 #define TRX_FLAGS_OFFSET 12
37 #define TRX_MAX_PARTS 3
38
39 struct trx_header {
40 uint32_t magic;
41 uint32_t length;
42 uint32_t crc32;
43 uint16_t flags;
44 uint16_t version;
45 uint32_t offset[3];
46 };
47
48 char *trx_path;
49 size_t trx_offset = 0;
50 char *partition[TRX_MAX_PARTS] = {};
51
52 static inline size_t otrx_min(size_t x, size_t y) {
53 return x < y ? x : y;
54 }
55
56 /**************************************************
57 * CRC32
58 **************************************************/
59
60 static const uint32_t crc32_tbl[] = {
61 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
62 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
63 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
64 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
65 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
66 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
67 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
68 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
69 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
70 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
71 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
72 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
73 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
74 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
75 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
76 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
77 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
78 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
79 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
80 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
81 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
82 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
83 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
84 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
85 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
86 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
87 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
88 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
89 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
90 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
91 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
92 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
93 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
94 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
95 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
96 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
97 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
98 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
99 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
100 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
101 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
102 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
103 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
104 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
105 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
106 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
107 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
108 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
109 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
110 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
111 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
112 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
113 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
114 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
115 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
116 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
117 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
118 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
119 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
120 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
121 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
122 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
123 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
124 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
125 };
126
127 uint32_t otrx_crc32(uint32_t crc, uint8_t *buf, size_t len) {
128 while (len) {
129 crc = crc32_tbl[(crc ^ *buf) & 0xff] ^ (crc >> 8);
130 buf++;
131 len--;
132 }
133
134 return crc;
135 }
136
137 /**************************************************
138 * Check
139 **************************************************/
140
141 static void otrx_check_parse_options(int argc, char **argv) {
142 int c;
143
144 while ((c = getopt(argc, argv, "o:")) != -1) {
145 switch (c) {
146 case 'o':
147 trx_offset = atoi(optarg);
148 break;
149 }
150 }
151 }
152
153 static int otrx_check(int argc, char **argv) {
154 FILE *trx;
155 struct trx_header hdr;
156 size_t bytes, length;
157 uint8_t buf[1024];
158 uint32_t crc32;
159 int err = 0;
160
161 if (argc < 3) {
162 fprintf(stderr, "No TRX file passed\n");
163 err = -EINVAL;
164 goto out;
165 }
166 trx_path = argv[2];
167
168 optind = 3;
169 otrx_check_parse_options(argc, argv);
170
171 trx = fopen(trx_path, "r");
172 if (!trx) {
173 fprintf(stderr, "Couldn't open %s\n", trx_path);
174 err = -EACCES;
175 goto out;
176 }
177
178 fseek(trx, trx_offset, SEEK_SET);
179 bytes = fread(&hdr, 1, sizeof(hdr), trx);
180 if (bytes != sizeof(hdr)) {
181 fprintf(stderr, "Couldn't read %s header\n", trx_path);
182 err = -EIO;
183 goto err_close;
184 }
185
186 if (le32_to_cpu(hdr.magic) != TRX_MAGIC) {
187 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic));
188 err = -EINVAL;
189 goto err_close;
190 }
191
192 length = le32_to_cpu(hdr.length);
193 if (length < sizeof(hdr)) {
194 fprintf(stderr, "Length read from TRX too low (%zu B)\n", length);
195 err = -EINVAL;
196 goto err_close;
197 }
198
199 crc32 = 0xffffffff;
200 fseek(trx, trx_offset + TRX_FLAGS_OFFSET, SEEK_SET);
201 length -= TRX_FLAGS_OFFSET;
202 while ((bytes = fread(buf, 1, otrx_min(sizeof(buf), length), trx)) > 0) {
203 crc32 = otrx_crc32(crc32, buf, bytes);
204 length -= bytes;
205 }
206
207 if (length) {
208 fprintf(stderr, "Couldn't read last %zd B of data from %s\n", length, trx_path);
209 err = -EIO;
210 goto err_close;
211 }
212
213 if (crc32 != le32_to_cpu(hdr.crc32)) {
214 fprintf(stderr, "Invalid data crc32: 0x%08x instead of 0x%08x\n", crc32, le32_to_cpu(hdr.crc32));
215 err = -EINVAL;
216 goto err_close;
217 }
218
219 printf("Found a valid TRX version %d\n", le32_to_cpu(hdr.version));
220
221 err_close:
222 fclose(trx);
223 out:
224 return err;
225 }
226
227 /**************************************************
228 * Create
229 **************************************************/
230
231 static ssize_t otrx_create_append_file(FILE *trx, const char *in_path) {
232 FILE *in;
233 size_t bytes;
234 ssize_t length = 0;
235 uint8_t buf[1024];
236
237 in = fopen(in_path, "r");
238 if (!in) {
239 fprintf(stderr, "Couldn't open %s\n", in_path);
240 return -EACCES;
241 }
242
243 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
244 if (fwrite(buf, 1, bytes, trx) != bytes) {
245 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, trx_path);
246 length = -EIO;
247 break;
248 }
249 length += bytes;
250 }
251
252 fclose(in);
253
254 return length;
255 }
256
257 static ssize_t otrx_create_append_zeros(FILE *trx, size_t length) {
258 uint8_t *buf;
259
260 buf = malloc(length);
261 if (!buf)
262 return -ENOMEM;
263 memset(buf, 0, length);
264
265 if (fwrite(buf, 1, length, trx) != length) {
266 fprintf(stderr, "Couldn't write %zu B to %s\n", length, trx_path);
267 free(buf);
268 return -EIO;
269 }
270
271 free(buf);
272
273 return length;
274 }
275
276 static ssize_t otrx_create_align(FILE *trx, size_t curr_offset, size_t alignment) {
277 if (curr_offset & (alignment - 1)) {
278 size_t length = alignment - (curr_offset % alignment);
279 return otrx_create_append_zeros(trx, length);
280 }
281
282 return 0;
283 }
284
285 static int otrx_create_write_hdr(FILE *trx, struct trx_header *hdr) {
286 size_t bytes, length;
287 uint8_t buf[1024];
288 uint32_t crc32;
289
290 hdr->version = 1;
291
292 fseek(trx, 0, SEEK_SET);
293 bytes = fwrite(hdr, 1, sizeof(struct trx_header), trx);
294 if (bytes != sizeof(struct trx_header)) {
295 fprintf(stderr, "Couldn't write TRX header to %s\n", trx_path);
296 return -EIO;
297 }
298
299 length = le32_to_cpu(hdr->length);
300
301 crc32 = 0xffffffff;
302 fseek(trx, TRX_FLAGS_OFFSET, SEEK_SET);
303 length -= TRX_FLAGS_OFFSET;
304 while ((bytes = fread(buf, 1, otrx_min(sizeof(buf), length), trx)) > 0) {
305 crc32 = otrx_crc32(crc32, buf, bytes);
306 length -= bytes;
307 }
308 hdr->crc32 = cpu_to_le32(crc32);
309
310 fseek(trx, 0, SEEK_SET);
311 bytes = fwrite(hdr, 1, sizeof(struct trx_header), trx);
312 if (bytes != sizeof(struct trx_header)) {
313 fprintf(stderr, "Couldn't write TRX header to %s\n", trx_path);
314 return -EIO;
315 }
316
317 return 0;
318 }
319
320 static int otrx_create(int argc, char **argv) {
321 FILE *trx;
322 struct trx_header hdr = {};
323 ssize_t sbytes;
324 size_t curr_idx = 0;
325 size_t curr_offset = sizeof(hdr);
326 char *e;
327 uint32_t magic;
328 int c;
329 int err = 0;
330
331 hdr.magic = cpu_to_le32(TRX_MAGIC);
332
333 if (argc < 3) {
334 fprintf(stderr, "No TRX file passed\n");
335 err = -EINVAL;
336 goto out;
337 }
338 trx_path = argv[2];
339
340 trx = fopen(trx_path, "w+");
341 if (!trx) {
342 fprintf(stderr, "Couldn't open %s\n", trx_path);
343 err = -EACCES;
344 goto out;
345 }
346 fseek(trx, curr_offset, SEEK_SET);
347
348 optind = 3;
349 while ((c = getopt(argc, argv, "f:A:a:b:M:")) != -1) {
350 switch (c) {
351 case 'f':
352 if (curr_idx >= TRX_MAX_PARTS) {
353 err = -ENOSPC;
354 fprintf(stderr, "Reached TRX partitions limit, no place for %s\n", optarg);
355 goto err_close;
356 }
357
358 sbytes = otrx_create_append_file(trx, optarg);
359 if (sbytes < 0) {
360 fprintf(stderr, "Failed to append file %s\n", optarg);
361 } else {
362 hdr.offset[curr_idx++] = curr_offset;
363 curr_offset += sbytes;
364 }
365
366 sbytes = otrx_create_align(trx, curr_offset, 4);
367 if (sbytes < 0)
368 fprintf(stderr, "Failed to append zeros\n");
369 else
370 curr_offset += sbytes;
371
372 break;
373 case 'A':
374 sbytes = otrx_create_append_file(trx, optarg);
375 if (sbytes < 0) {
376 fprintf(stderr, "Failed to append file %s\n", optarg);
377 } else {
378 curr_offset += sbytes;
379 }
380
381 sbytes = otrx_create_align(trx, curr_offset, 4);
382 if (sbytes < 0)
383 fprintf(stderr, "Failed to append zeros\n");
384 else
385 curr_offset += sbytes;
386 break;
387 case 'a':
388 sbytes = otrx_create_align(trx, curr_offset, strtol(optarg, NULL, 0));
389 if (sbytes < 0)
390 fprintf(stderr, "Failed to append zeros\n");
391 else
392 curr_offset += sbytes;
393 break;
394 case 'b':
395 sbytes = strtol(optarg, NULL, 0) - curr_offset;
396 if (sbytes < 0) {
397 fprintf(stderr, "Current TRX length is 0x%zx, can't pad it with zeros to 0x%lx\n", curr_offset, strtol(optarg, NULL, 0));
398 } else {
399 sbytes = otrx_create_append_zeros(trx, sbytes);
400 if (sbytes < 0)
401 fprintf(stderr, "Failed to append zeros\n");
402 else
403 curr_offset += sbytes;
404 }
405 break;
406 case 'M':
407 errno = 0;
408 magic = strtoul(optarg, &e, 0);
409 if (errno || (e == optarg) || *e)
410 fprintf(stderr, "illegal magic string %s\n", optarg);
411 else
412 hdr.magic = cpu_to_le32(magic);
413 break;
414 }
415 if (err)
416 break;
417 }
418
419 sbytes = otrx_create_align(trx, curr_offset, 0x1000);
420 if (sbytes < 0)
421 fprintf(stderr, "Failed to append zeros\n");
422 else
423 curr_offset += sbytes;
424
425 hdr.length = curr_offset;
426 otrx_create_write_hdr(trx, &hdr);
427 err_close:
428 fclose(trx);
429 out:
430 return err;
431 }
432
433 /**************************************************
434 * Extract
435 **************************************************/
436
437 static void otrx_extract_parse_options(int argc, char **argv) {
438 int c;
439
440 while ((c = getopt(argc, argv, "c:e:o:1:2:3:")) != -1) {
441 switch (c) {
442 case 'o':
443 trx_offset = atoi(optarg);
444 break;
445 case '1':
446 partition[0] = optarg;
447 break;
448 case '2':
449 partition[1] = optarg;
450 break;
451 case '3':
452 partition[2] = optarg;
453 break;
454 }
455 }
456 }
457
458 static int otrx_extract_copy(FILE *trx, size_t offset, size_t length, char *out_path) {
459 FILE *out;
460 size_t bytes;
461 uint8_t *buf;
462 int err = 0;
463
464 out = fopen(out_path, "w");
465 if (!out) {
466 fprintf(stderr, "Couldn't open %s\n", out_path);
467 err = -EACCES;
468 goto out;
469 }
470
471 buf = malloc(length);
472 if (!buf) {
473 fprintf(stderr, "Couldn't alloc %zu B buffer\n", length);
474 err = -ENOMEM;
475 goto err_close;
476 }
477
478 fseek(trx, offset, SEEK_SET);
479 bytes = fread(buf, 1, length, trx);
480 if (bytes != length) {
481 fprintf(stderr, "Couldn't read %zu B of data from %s\n", length, trx_path);
482 err = -ENOMEM;
483 goto err_free_buf;
484 };
485
486 bytes = fwrite(buf, 1, length, out);
487 if (bytes != length) {
488 fprintf(stderr, "Couldn't write %zu B to %s\n", length, out_path);
489 err = -ENOMEM;
490 goto err_free_buf;
491 }
492
493 printf("Extracted 0x%zx bytes into %s\n", length, out_path);
494
495 err_free_buf:
496 free(buf);
497 err_close:
498 fclose(out);
499 out:
500 return err;
501 }
502
503 static int otrx_extract(int argc, char **argv) {
504 FILE *trx;
505 struct trx_header hdr;
506 size_t bytes;
507 int i;
508 int err = 0;
509
510 if (argc < 3) {
511 fprintf(stderr, "No TRX file passed\n");
512 err = -EINVAL;
513 goto out;
514 }
515 trx_path = argv[2];
516
517 optind = 3;
518 otrx_extract_parse_options(argc, argv);
519
520 trx = fopen(trx_path, "r");
521 if (!trx) {
522 fprintf(stderr, "Couldn't open %s\n", trx_path);
523 err = -EACCES;
524 goto out;
525 }
526
527 fseek(trx, trx_offset, SEEK_SET);
528 bytes = fread(&hdr, 1, sizeof(hdr), trx);
529 if (bytes != sizeof(hdr)) {
530 fprintf(stderr, "Couldn't read %s header\n", trx_path);
531 err = -EIO;
532 goto err_close;
533 }
534
535 if (le32_to_cpu(hdr.magic) != TRX_MAGIC) {
536 fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic));
537 err = -EINVAL;
538 goto err_close;
539 }
540
541 for (i = 0; i < TRX_MAX_PARTS; i++) {
542 size_t length;
543
544 if (!partition[i])
545 continue;
546 if (!hdr.offset[i]) {
547 printf("TRX doesn't contain partition %d, can't extract %s\n", i + 1, partition[i]);
548 continue;
549 }
550
551 if (i + 1 >= TRX_MAX_PARTS || !hdr.offset[i + 1])
552 length = le32_to_cpu(hdr.length) - le32_to_cpu(hdr.offset[i]);
553 else
554 length = le32_to_cpu(hdr.offset[i + 1]) - le32_to_cpu(hdr.offset[i]);
555
556 otrx_extract_copy(trx, trx_offset + le32_to_cpu(hdr.offset[i]), length, partition[i]);
557 }
558
559 err_close:
560 fclose(trx);
561 out:
562 return err;
563 }
564
565 /**************************************************
566 * Start
567 **************************************************/
568
569 static void usage() {
570 printf("Usage:\n");
571 printf("\n");
572 printf("Checking TRX file:\n");
573 printf("\totrx check <file> [options]\tcheck if file is a valid TRX\n");
574 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
575 printf("\n");
576 printf("Creating new TRX file:\n");
577 printf("\totrx create <file> [options] [partitions]\n");
578 printf("\t-f file\t\t\t\t[partition] start new partition with content copied from file\n");
579 printf("\t-A file\t\t\t\t[partition] append current partition with content copied from file\n");
580 printf("\t-a alignment\t\t\t[partition] align current partition\n");
581 printf("\t-b offset\t\t\t[partition] append zeros to partition till reaching absolute offset\n");
582 printf("\n");
583 printf("Extracting from TRX file:\n");
584 printf("\totrx extract <file> [options]\textract partitions from TRX file\n");
585 printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n");
586 printf("\t-1 file\t\t\t\tfile to extract 1st partition to (optional)\n");
587 printf("\t-2 file\t\t\t\tfile to extract 2nd partition to (optional)\n");
588 printf("\t-3 file\t\t\t\tfile to extract 3rd partition to (optional)\n");
589 }
590
591 int main(int argc, char **argv) {
592 if (argc > 1) {
593 if (!strcmp(argv[1], "check"))
594 return otrx_check(argc, argv);
595 else if (!strcmp(argv[1], "create"))
596 return otrx_create(argc, argv);
597 else if (!strcmp(argv[1], "extract"))
598 return otrx_extract(argc, argv);
599 }
600
601 usage();
602 return 0;
603 }