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