tplink-safeloader: add Archer A6 v2 (RU)
[project/firmware-utils.git] / src / lxlfw.c
1 // SPDX-License-Identifier: GPL-2.0-or-later OR MIT
2 /*
3 * Luxul's firmware container format
4 *
5 * Copyright 2020 Legrand AV Inc.
6 */
7
8 #define _GNU_SOURCE
9
10 #include <byteswap.h>
11 #include <endian.h>
12 #include <errno.h>
13 #include <libgen.h>
14 #include <stddef.h>
15 #include <stdint.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20
21 #if __BYTE_ORDER == __BIG_ENDIAN
22 #define cpu_to_le32(x) bswap_32(x)
23 #define cpu_to_le16(x) bswap_16(x)
24 #define le32_to_cpu(x) bswap_32(x)
25 #define le16_to_cpu(x) bswap_16(x)
26 #elif __BYTE_ORDER == __LITTLE_ENDIAN
27 #define cpu_to_le32(x) (x)
28 #define cpu_to_le16(x) (x)
29 #define le32_to_cpu(x) (x)
30 #define le16_to_cpu(x) (x)
31 #endif
32
33 #define min(a, b) \
34 ({ \
35 __typeof__ (a) _a = (a); \
36 __typeof__ (b) _b = (b); \
37 _a < _b ? _a : _b; \
38 })
39
40 #define max(a, b) \
41 ({ \
42 __typeof__ (a) _a = (a); \
43 __typeof__ (b) _b = (b); \
44 _a > _b ? _a : _b; \
45 })
46
47 #define MAX_SUPPORTED_VERSION 3
48
49 #define LXL_FLAGS_VENDOR_LUXUL 0x00000001
50
51 #define LXL_BLOB_CERTIFICATE 0x0001
52 #define LXL_BLOB_SIGNATURE 0x0002
53
54 struct lxl_hdr {
55 char magic[4]; /* "LXL#" */
56 uint32_t version;
57 uint32_t hdr_len;
58 uint8_t v0_end[0];
59 /* Version: 1+ */
60 uint32_t flags;
61 char board[16];
62 uint8_t v1_end[0];
63 /* Version: 2+ */
64 uint8_t release[4];
65 uint8_t v2_end[0];
66 /* Version: 3+ */
67 uint32_t blobs_offset;
68 uint32_t blobs_len;
69 uint8_t v3_end[0];
70 } __attribute__((packed));
71
72 struct lxl_blob {
73 char magic[2]; /* "D#" */
74 uint16_t type;
75 uint32_t len;
76 uint8_t data[0];
77 } __attribute__((packed));
78
79 /**************************************************
80 * Helpers
81 **************************************************/
82
83 static uint32_t lxlfw_hdr_len(uint32_t version)
84 {
85 switch (version) {
86 case 0:
87 return offsetof(struct lxl_hdr, v0_end);
88 case 1:
89 return offsetof(struct lxl_hdr, v1_end);
90 case 2:
91 return offsetof(struct lxl_hdr, v2_end);
92 case 3:
93 return offsetof(struct lxl_hdr, v3_end);
94 default:
95 fprintf(stderr, "Unsupported version %d\n", version);
96 return 0;
97 }
98 }
99
100 /**
101 * lxlfw_open - open Luxul firmware file and validate it
102 *
103 * @pathname: Luxul firmware file
104 * @hdr: struct to read to
105 */
106 static FILE *lxlfw_open(const char *pathname, struct lxl_hdr *hdr)
107 {
108 size_t v0_len = lxlfw_hdr_len(0);
109 size_t min_hdr_len;
110 uint32_t version;
111 uint32_t hdr_len;
112 size_t bytes;
113 FILE *lxl;
114
115 lxl = fopen(pathname, "r");
116 if (!lxl) {
117 fprintf(stderr, "Could not open \"%s\" file\n", pathname);
118 goto err_out;
119 }
120
121 bytes = fread(hdr, 1, v0_len, lxl);
122 if (bytes != v0_len) {
123 fprintf(stderr, "Input file too small to use Luxul format\n");
124 goto err_close;
125 }
126
127 if (memcmp(hdr->magic, "LXL#", 4)) {
128 fprintf(stderr, "File <file> does not use Luxul's format\n");
129 goto err_close;
130 }
131
132 version = le32_to_cpu(hdr->version);
133
134 min_hdr_len = lxlfw_hdr_len(min(version, MAX_SUPPORTED_VERSION));
135
136 bytes = fread(((uint8_t *)hdr) + v0_len, 1, min_hdr_len - v0_len, lxl);
137 if (bytes != min_hdr_len - v0_len) {
138 fprintf(stderr, "Input file too small for header version %d\n", version);
139 goto err_close;
140 }
141
142 hdr_len = le32_to_cpu(hdr->hdr_len);
143
144 if (hdr_len < min_hdr_len) {
145 fprintf(stderr, "Header length mismatch: 0x%x (expected: 0x%zx)\n", hdr_len, min_hdr_len);
146 goto err_close;
147 }
148
149 if (version >= 3 && hdr->blobs_offset && hdr->blobs_len) {
150 uint32_t blobs_end = le32_to_cpu(hdr->blobs_offset) + le32_to_cpu(hdr->blobs_len);
151
152 if (blobs_end > hdr_len) {
153 fprintf(stderr, "Blobs section ends beyond header end: 0x%x (max: 0x%x)\n", blobs_end, hdr_len);
154 goto err_close;
155 }
156 }
157
158 return lxl;
159
160 err_close:
161 fclose(lxl);
162 err_out:
163 return NULL;
164 }
165
166 /**
167 * lxlfw_copy_data - read data from one stream and write to another
168 *
169 * @from: input stream
170 * @to: output stream
171 * @size: amount of bytes to copy (0 to copy all data)
172 */
173 static ssize_t lxlfw_copy_data(FILE *from, FILE *to, size_t size)
174 {
175 int copy_all = size == 0;
176 char buf[512];
177 size_t ret = 0;
178
179 while (copy_all || size) {
180 size_t to_read = copy_all ? sizeof(buf) : min(size, sizeof(buf));
181 size_t bytes;
182
183 bytes = fread(buf, 1, to_read, from);
184 if (bytes == 0 && copy_all) {
185 break;
186 } else if (bytes <= 0) {
187 fprintf(stderr, "Failed to read data\n");
188 return -EIO;
189 }
190
191 if (fwrite(buf, 1, bytes, to) != bytes) {
192 fprintf(stderr, "Failed to write data\n");
193 return -EIO;
194 }
195
196 if (!copy_all)
197 size -= bytes;
198 ret += bytes;
199 }
200
201 return ret;
202 }
203
204 /**
205 * lxlfw_write_blob - read data from external file and write blob to stream
206 *
207 * @lxl: stream to write to
208 * @type: blob type
209 * @pathname: external file pathname to read blob data from
210 */
211 static ssize_t lxlfw_write_blob(FILE *lxl, uint16_t type, const char *pathname)
212 {
213 struct lxl_blob blob = {
214 .magic = { 'D', '#' },
215 .type = cpu_to_le16(type),
216 };
217 char buf[512];
218 size_t blob_data_len;
219 size_t bytes;
220 FILE *data;
221
222 data = fopen(pathname, "r");
223 if (!data) {
224 fprintf(stderr, "Could not open input file %s\n", pathname);
225 return -EIO;
226 }
227
228 blob_data_len = 0;
229 fseek(lxl, sizeof(blob), SEEK_CUR);
230 while ((bytes = fread(buf, 1, sizeof(buf), data)) > 0) {
231 if (fwrite(buf, 1, bytes, lxl) != bytes) {
232 fprintf(stderr, "Could not copy %zu bytes from input file\n", bytes);
233 fclose(data);
234 return -EIO;
235 }
236 blob_data_len += bytes;
237 }
238
239 fclose(data);
240
241 blob.len = cpu_to_le32(blob_data_len);
242
243 fseek(lxl, -(blob_data_len + sizeof(blob)), SEEK_CUR);
244 bytes = fwrite(&blob, 1, sizeof(blob), lxl);
245 if (bytes != sizeof(blob)) {
246 fprintf(stderr, "Could not write Luxul's header\n");
247 return -EIO;
248 }
249
250 fseek(lxl, blob_data_len, SEEK_CUR);
251
252 return blob_data_len + sizeof(blob);
253 }
254
255 /**************************************************
256 * Info
257 **************************************************/
258
259 static int lxlfw_info(int argc, char **argv) {
260 struct lxl_hdr hdr;
261 uint32_t version;
262 char board[17];
263 int err = 0;
264 FILE *lxl;
265 int flags;
266
267 if (argc < 3) {
268 fprintf(stderr, "Missing <file> argument\n");
269 err = -EINVAL;
270 goto out;
271 }
272
273 lxl = lxlfw_open(argv[2], &hdr);
274 if (!lxl) {
275 fprintf(stderr, "Could not open \"%s\" Luxul firmware\n", argv[2]);
276 err = -ENOENT;
277 goto out;
278 }
279
280 version = le32_to_cpu(hdr.version);
281
282 printf("Format version:\t%d\n", version);
283 printf("Header length:\t%d\n", le32_to_cpu(hdr.hdr_len));
284 if (version >= 1) {
285 printf("Flags:\t\t");
286 flags = le32_to_cpu(hdr.flags);
287 if (flags & LXL_FLAGS_VENDOR_LUXUL)
288 printf("VENDOR_LUXUL ");
289 printf("\n");
290 memcpy(board, hdr.board, sizeof(hdr.board));
291 board[16] = '\0';
292 printf("Board:\t\t%s\n", board);
293 }
294 if (version >= 2) {
295 printf("Release:\t");
296 if (hdr.release[0] || hdr.release[1] || hdr.release[2] || hdr.release[3]) {
297 printf("%hu.%hu.%hu", hdr.release[0], hdr.release[1], hdr.release[2]);
298 if (hdr.release[3])
299 printf(".%hu", hdr.release[3]);
300 }
301 printf("\n");
302 }
303 if (version >= 3) {
304 printf("Blobs offset:\t%d\n", le32_to_cpu(hdr.blobs_offset));
305 printf("Blobs length:\t%d\n", le32_to_cpu(hdr.blobs_len));
306 }
307
308 if (version >= 3 && hdr.blobs_offset) {
309 size_t offset;
310
311 fseek(lxl, le32_to_cpu(hdr.blobs_offset), SEEK_SET);
312 for (offset = 0; offset < le32_to_cpu(hdr.blobs_len); ) {
313 struct lxl_blob blob;
314 size_t bytes;
315 size_t len;
316
317 bytes = fread(&blob, 1, sizeof(blob), lxl);
318 if (bytes != sizeof(blob)) {
319 fprintf(stderr, "Failed to read blob section\n");
320 err = -ENXIO;
321 goto err_close;
322 }
323
324 len = le32_to_cpu(blob.len);
325
326 printf("\n");
327 printf("Blob\n");
328 printf("Magic:\t\t%s\n", blob.magic);
329 printf("Type:\t\t0x%04x\n", le16_to_cpu(blob.type));
330 printf("Length:\t\t%zu\n", len);
331
332 offset += sizeof(blob) + len;
333 fseek(lxl, len, SEEK_CUR);
334 }
335
336 if (offset != le32_to_cpu(hdr.blobs_len)) {
337 printf("\n");
338 fprintf(stderr, "Blobs size (0x%zx) doesn't match declared length (0x%x)\n", offset, le32_to_cpu(hdr.blobs_len));
339 }
340 }
341
342 err_close:
343 fclose(lxl);
344 out:
345 return err;
346 }
347
348 /**************************************************
349 * Extract
350 **************************************************/
351
352 static int lxlfw_extract(int argc, char **argv) {
353 struct lxl_hdr hdr;
354 char *out_path = NULL;
355 ssize_t bytes;
356 int err = 0;
357 FILE *lxl;
358 FILE *out;
359 int c;
360
361 if (argc < 3) {
362 fprintf(stderr, "Missing <file> argument\n");
363 err = -EINVAL;
364 goto out;
365 }
366
367 optind = 3;
368 while ((c = getopt(argc, argv, "O:")) != -1) {
369 switch (c) {
370 case 'O':
371 out_path = optarg;
372 break;
373 }
374 }
375
376 if (!out_path) {
377 fprintf(stderr, "Missing output file path\n");
378 err = -EINVAL;
379 goto out;
380 }
381
382 lxl = lxlfw_open(argv[2], &hdr);
383 if (!lxl) {
384 fprintf(stderr, "Failed to open \"%s\" Luxul firmware\n", argv[2]);
385 err = -ENOENT;
386 goto out;
387 }
388
389 fseek(lxl, le32_to_cpu(hdr.hdr_len), SEEK_SET);
390
391 if (!strcmp(out_path, "-")) {
392 out = stdout;
393 } else {
394 out = fopen(out_path, "w+");
395 if (!out) {
396 fprintf(stderr, "Failed to open \"%s\" file\n", out_path);
397 err = -EIO;
398 goto err_close_lxl;
399 }
400 }
401
402 bytes = lxlfw_copy_data(lxl, out, 0);
403 if (bytes < 0) {
404 fprintf(stderr, "Failed to copy image: %zd\n", bytes);
405 err = -EIO;
406 goto err_close_lxl;
407 }
408
409 if (out != stdout) {
410 fclose(out);
411 }
412
413 err_close_lxl:
414 fclose(lxl);
415 out:
416 return err;
417 }
418
419 /**************************************************
420 * Blobs
421 **************************************************/
422
423 /**
424 * lxlfw_blob_save - save blob data to external file
425 *
426 * @lxl: Luxul firmware FILE with position seeked to blob data
427 * @len: blob data length
428 * @path: external file pathname to write
429 */
430 static int lxlfw_blob_save(FILE *lxl, size_t len, const char *path) {
431 char buf[256];
432 size_t bytes;
433 FILE *out;
434 int err = 0;
435
436 out = fopen(path, "w+");
437 if (!out) {
438 fprintf(stderr, "Could not open \"%s\" file\n", path);
439 err = -EIO;
440 goto err_out;
441 }
442
443 while (len && (bytes = fread(buf, 1, min(len, sizeof(buf)), lxl)) > 0) {
444 if (fwrite(buf, 1, bytes, out) != bytes) {
445 fprintf(stderr, "Could not copy %zu bytes from input file\n", bytes);
446 err = -EIO;
447 goto err_close_out;
448 }
449 len -= bytes;
450 }
451
452 if (len) {
453 fprintf(stderr, "Could not copy all signature\n");
454 err = -EIO;
455 goto err_close_out;
456 }
457
458 err_close_out:
459 fclose(out);
460 err_out:
461 return err;
462 }
463
464 static int lxlfw_blobs(int argc, char **argv) {
465 char *certificate_path = NULL;
466 char *signature_path = NULL;
467 struct lxl_hdr hdr;
468 uint32_t version;
469 size_t offset;
470 size_t bytes;
471 int err = 0;
472 FILE *lxl;
473 int c;
474
475 if (argc < 3) {
476 fprintf(stderr, "Missing <file> argument\n");
477 err = -EINVAL;
478 goto out;
479 }
480
481 optind = 3;
482 while ((c = getopt(argc, argv, "c:s:")) != -1) {
483 switch (c) {
484 case 'c':
485 certificate_path = optarg;
486 break;
487 case 's':
488 signature_path = optarg;
489 break;
490 }
491 }
492
493 if (!certificate_path && !signature_path) {
494 fprintf(stderr, "Missing info on blobs to extract\n");
495 err = -EINVAL;
496 goto out;
497 }
498
499 lxl = lxlfw_open(argv[2], &hdr);
500 if (!lxl) {
501 fprintf(stderr, "Failed to open \"%s\" Luxul firmware\n", argv[2]);
502 err = -ENOENT;
503 goto out;
504 }
505
506 version = le32_to_cpu(hdr.version);
507
508 if (version < 3 || !hdr.blobs_offset) {
509 fprintf(stderr, "File <file> doesn't contain any blobs\n");
510 err = -ENOENT;
511 goto err_close;
512 }
513
514 fseek(lxl, le32_to_cpu(hdr.blobs_offset), SEEK_SET);
515 for (offset = 0; offset < le32_to_cpu(hdr.blobs_len); ) {
516 struct lxl_blob blob;
517 uint16_t type;
518 size_t len;
519
520 bytes = fread(&blob, 1, sizeof(blob), lxl);
521 if (bytes != sizeof(blob)) {
522 fprintf(stderr, "Failed to read blob section\n");
523 err = -ENXIO;
524 goto err_close;
525 }
526 offset += bytes;
527
528 if (memcmp(blob.magic, "D#", 2)) {
529 fprintf(stderr, "Failed to parse blob section\n");
530 err = -ENXIO;
531 goto err_close;
532 }
533
534 type = le16_to_cpu(blob.type);
535 len = le32_to_cpu(blob.len);
536
537 if (type == LXL_BLOB_CERTIFICATE && certificate_path) {
538 err = lxlfw_blob_save(lxl, len, certificate_path);
539 certificate_path = NULL;
540 } else if (type == LXL_BLOB_SIGNATURE && signature_path) {
541 err = lxlfw_blob_save(lxl, len, signature_path);
542 signature_path = NULL;
543 } else {
544 fseek(lxl, len, SEEK_CUR);
545 }
546 if (err) {
547 fprintf(stderr, "Failed to save blob section\n");
548 goto err_close;
549 }
550 offset += len;
551 }
552
553 if (certificate_path) {
554 fprintf(stderr, "Failed to find certificate blob\n");
555 err = -ENOENT;
556 }
557 if (signature_path) {
558 fprintf(stderr, "Failed to find signature blob\n");
559 err = -ENOENT;
560 }
561
562 err_close:
563 fclose(lxl);
564 out:
565 return err;
566 }
567
568 /**************************************************
569 * Create
570 **************************************************/
571
572 static int lxlfw_create(int argc, char **argv) {
573 struct lxl_hdr hdr = {
574 .magic = { 'L', 'X', 'L', '#' },
575 };
576 char *certificate_path = NULL;
577 char *signature_path = NULL;
578 char *in_path = NULL;
579 uint32_t version = 0;
580 uint32_t hdr_raw_len; /* Header length without blobs */
581 uint32_t hdr_len; /* Header length with blobs */
582 uint32_t blobs_len;
583 ssize_t bytes;
584 int err = 0;
585 FILE *lxl;
586 FILE *in;
587 int c;
588
589 if (argc < 3) {
590 fprintf(stderr, "Missing <file> argument\n");
591 err = -EINVAL;
592 goto out;
593 }
594
595 optind = 3;
596 while ((c = getopt(argc, argv, "i:lb:r:")) != -1) {
597 switch (c) {
598 case 'i':
599 in_path = optarg;
600 break;
601 case 'l':
602 hdr.flags |= cpu_to_le32(LXL_FLAGS_VENDOR_LUXUL);
603 version = max(version, 1);
604 break;
605 case 'b':
606 memcpy(hdr.board, optarg, strlen(optarg) > 16 ? 16 : strlen(optarg));
607 version = max(version, 1);
608 break;
609 case 'r':
610 if (sscanf(optarg, "%hhu.%hhu.%hhu.%hhu", &hdr.release[0], &hdr.release[1], &hdr.release[2], &hdr.release[3]) < 1) {
611 fprintf(stderr, "Failed to parse release number \"%s\"\n", optarg);
612 err = -EINVAL;
613 goto out;
614 }
615 version = max(version, 2);
616 break;
617 case 'c':
618 certificate_path = optarg;
619 version = max(version, 3);
620 break;
621 case 's':
622 signature_path = optarg;
623 version = max(version, 3);
624 break;
625 }
626 }
627
628 hdr_raw_len = lxlfw_hdr_len(version);
629 hdr_len = hdr_raw_len;
630
631 if (!in_path) {
632 fprintf(stderr, "Missing input file argument\n");
633 err = -EINVAL;
634 goto out;
635 }
636
637 in = fopen(in_path, "r");
638 if (!in) {
639 fprintf(stderr, "Could not open input file %s\n", in_path);
640 err = -EIO;
641 goto out;
642 }
643
644 lxl = fopen(argv[2], "w+");
645 if (!lxl) {
646 fprintf(stderr, "Could not open \"%s\" file\n", argv[2]);
647 err = -EIO;
648 goto err_close_in;
649 }
650
651 /* Write blobs */
652
653 blobs_len = 0;
654
655 fseek(lxl, hdr_raw_len, SEEK_SET);
656 if (certificate_path) {
657 bytes = lxlfw_write_blob(lxl, LXL_BLOB_CERTIFICATE, certificate_path);
658 if (bytes <= 0) {
659 fprintf(stderr, "Failed to write certificate\n");
660 goto err_close_lxl;
661 }
662 blobs_len += bytes;
663 }
664 if (signature_path) {
665 bytes = lxlfw_write_blob(lxl, LXL_BLOB_SIGNATURE, signature_path);
666 if (bytes <= 0) {
667 fprintf(stderr, "Failed to write signature\n");
668 goto err_close_lxl;
669 }
670 blobs_len += bytes;
671 }
672
673 if (blobs_len) {
674 hdr.blobs_offset = cpu_to_le32(hdr_raw_len);
675 hdr.blobs_len = cpu_to_le32(blobs_len);
676 hdr_len += blobs_len;
677 }
678
679 /* Write header */
680
681 hdr.version = cpu_to_le32(version);
682 hdr.hdr_len = cpu_to_le32(hdr_len);
683
684 fseek(lxl, 0, SEEK_SET);
685 bytes = fwrite(&hdr, 1, hdr_raw_len, lxl);
686 if (bytes != hdr_raw_len) {
687 fprintf(stderr, "Could not write Luxul's header\n");
688 err = -EIO;
689 goto err_close_lxl;
690 }
691
692 /* Write input data */
693
694 fseek(lxl, 0, SEEK_END);
695 bytes = lxlfw_copy_data(in, lxl, 0);
696 if (bytes < 0) {
697 fprintf(stderr, "Could not copy %zu bytes from input file\n", bytes);
698 err = -EIO;
699 goto err_close_lxl;
700 }
701
702 err_close_lxl:
703 fclose(lxl);
704 err_close_in:
705 fclose(in);
706 out:
707 return err;
708 }
709
710 /**************************************************
711 * Insert
712 **************************************************/
713
714 static int lxlfw_insert(int argc, char **argv) {
715 struct lxl_hdr hdr = { };
716 char *certificate_path = NULL;
717 char *signature_path = NULL;
718 char *tmp_path = NULL;
719 uint32_t version = 0;
720 uint32_t hdr_raw_len; /* Header length without blobs */
721 uint32_t hdr_len; /* Header length with blobs */
722 uint32_t blobs_len;
723 ssize_t bytes;
724 char *path;
725 FILE *lxl;
726 FILE *tmp;
727 int fd;
728 int c;
729 int err = 0;
730
731 if (argc < 3) {
732 fprintf(stderr, "Missing <file> argument\n");
733 err = -EINVAL;
734 goto out;
735 }
736
737 optind = 3;
738 while ((c = getopt(argc, argv, "c:s:")) != -1) {
739 switch (c) {
740 case 'c':
741 certificate_path = optarg;
742 break;
743 case 's':
744 signature_path = optarg;
745 break;
746 }
747 }
748
749 if (!certificate_path && !signature_path) {
750 fprintf(stderr, "Missing info on blobs to insert\n");
751 err = -EINVAL;
752 goto out;
753 }
754
755 lxl = lxlfw_open(argv[2], &hdr);
756 if (!lxl) {
757 fprintf(stderr, "Failed to open \"%s\" Luxul firmware\n", argv[2]);
758 err = -ENOENT;
759 goto out;
760 }
761
762 version = le32_to_cpu(hdr.version);
763 if (version > MAX_SUPPORTED_VERSION) {
764 fprintf(stderr, "Unsupported <file> version %d\n", version);
765 err = -EIO;
766 goto err_close_lxl;
767 }
768
769 version = max(version, 3);
770
771 hdr_raw_len = lxlfw_hdr_len(version);
772 hdr_len = hdr_raw_len;
773
774 /* Temporary file */
775
776 path = strdup(argv[2]);
777 if (!path) {
778 err = -ENOMEM;
779 goto err_close_lxl;
780 }
781 asprintf(&tmp_path, "%s/lxlfwXXXXXX", dirname(path));
782 free(path);
783 if (!tmp_path) {
784 err = -ENOMEM;
785 goto err_close_lxl;
786 }
787
788 fd = mkstemp(tmp_path);
789 if (fd < 0) {
790 err = -errno;
791 fprintf(stderr, "Failed to open temporary file\n");
792 goto err_free_path;
793 }
794 tmp = fdopen(fd, "w+");
795
796 /* Blobs */
797
798 fseek(tmp, hdr_raw_len, SEEK_SET);
799 blobs_len = 0;
800
801 /* Copy old blobs */
802
803 if (hdr.blobs_offset) {
804 size_t offset;
805
806 fseek(lxl, le32_to_cpu(hdr.blobs_offset), SEEK_SET);
807 for (offset = 0; offset < le32_to_cpu(hdr.blobs_len); ) {
808 struct lxl_blob blob;
809 uint16_t type;
810 size_t len;
811
812 bytes = fread(&blob, 1, sizeof(blob), lxl);
813 if (bytes != sizeof(blob)) {
814 fprintf(stderr, "Failed to read blob section\n");
815 err = -ENXIO;
816 goto err_close_tmp;
817 }
818
819 type = le16_to_cpu(blob.type);
820 len = le32_to_cpu(blob.len);
821
822 /* Don't copy blobs that have to be replaced */
823 if ((type == LXL_BLOB_CERTIFICATE && certificate_path) ||
824 (type == LXL_BLOB_SIGNATURE && signature_path)) {
825 fseek(lxl, len, SEEK_CUR);
826 } else {
827 fseek(lxl, -sizeof(blob), SEEK_CUR);
828 bytes = lxlfw_copy_data(lxl, tmp, sizeof(blob) + len);
829 if (bytes != sizeof(blob) + len) {
830 fprintf(stderr, "Failed to copy original blob\n");
831 err = -EIO;
832 goto err_close_tmp;
833 }
834 blobs_len += sizeof(blob) + len;
835 }
836
837 offset += sizeof(blob) + len;
838 }
839 }
840
841 /* Write new blobs */
842
843 if (certificate_path) {
844 bytes = lxlfw_write_blob(tmp, LXL_BLOB_CERTIFICATE, certificate_path);
845 if (bytes <= 0) {
846 fprintf(stderr, "Failed to write certificate\n");
847 goto err_close_tmp;
848 }
849 blobs_len += bytes;
850 }
851 if (signature_path) {
852 bytes = lxlfw_write_blob(tmp, LXL_BLOB_SIGNATURE, signature_path);
853 if (bytes <= 0) {
854 fprintf(stderr, "Failed to write signature\n");
855 goto err_close_tmp;
856 }
857 blobs_len += bytes;
858 }
859
860 hdr.blobs_offset = cpu_to_le32(hdr_raw_len);
861 hdr.blobs_len = cpu_to_le32(blobs_len);
862 hdr_len += blobs_len;
863
864 /* Write header */
865
866 hdr.version = cpu_to_le32(version);
867 hdr.hdr_len = cpu_to_le32(hdr_len);
868
869 fseek(tmp, 0, SEEK_SET);
870 bytes = fwrite(&hdr, 1, hdr_raw_len, tmp);
871 if (bytes != hdr_raw_len) {
872 fprintf(stderr, "Could not write Luxul's header\n");
873 err = -EIO;
874 goto err_close_tmp;
875 }
876
877 /* Write original data */
878
879 fseek(tmp, 0, SEEK_END);
880 bytes = lxlfw_copy_data(lxl, tmp, 0);
881 if (bytes < 0) {
882 fprintf(stderr, "Failed to copy original file\n");
883 err = -EIO;
884 goto err_close_tmp;
885 }
886
887 fclose(tmp);
888
889 fclose(lxl);
890
891 /* Replace original file */
892
893 if (rename(tmp_path, argv[2])) {
894 err = -errno;
895 fprintf(stderr, "Failed to rename %s: %d\n", tmp_path, err);
896 unlink(tmp_path);
897 goto out;
898 }
899
900 return 0;
901
902 err_close_tmp:
903 fclose(tmp);
904 err_free_path:
905 free(tmp_path);
906 err_close_lxl:
907 fclose(lxl);
908 out:
909 return err;
910 }
911
912 /**************************************************
913 * Start
914 **************************************************/
915
916 static void usage() {
917 printf("Usage:\n");
918 printf("\n");
919 printf("Get info about Luxul firmware:\n");
920 printf("\tlxlfw info <file>\n");
921 printf("\n");
922 printf("Extract image from Luxul firmware:\n");
923 printf("\tlxlfw extract <file> [options]\n");
924 printf("\t-O file\t\t\t\toutput file (- for stdout)\n");
925 printf("\n");
926 printf("Extract blobs from Luxul firmware:\n");
927 printf("\tlxlfw blobs <file> [options]\n");
928 printf("\t-c file\t\t\t\tcertificate output file\n");
929 printf("\t-s file\t\t\t\tsignature output file\n");
930 printf("\n");
931 printf("Create new Luxul firmware:\n");
932 printf("\tlxlfw create <file> [options]\n");
933 printf("\t-i file\t\t\t\tinput file for Luxul's firmware container\n");
934 printf("\t-l\t\t\t\tmark firmware as created by Luxul company (DON'T USE)\n");
935 printf("\t-b board\t\t\tboard (device) name\n");
936 printf("\t-r release\t\t\trelease number (e.g. 5.1.0, 7.1.0.2)\n");
937 printf("\t-c file\t\t\t\tcertificate file\n");
938 printf("\t-s file\t\t\t\tsignature file\n");
939 printf("\n");
940 printf("Insert blob to Luxul firmware:\n");
941 printf("\tlxlfw insert <file> [options]\n");
942 printf("\t-c file\t\t\t\tcertificate file\n");
943 printf("\t-s file\t\t\t\tsignature file\n");
944
945 }
946
947 int main(int argc, char **argv) {
948 if (argc > 1) {
949 if (!strcmp(argv[1], "info"))
950 return lxlfw_info(argc, argv);
951 else if (!strcmp(argv[1], "extract"))
952 return lxlfw_extract(argc, argv);
953 else if (!strcmp(argv[1], "blobs"))
954 return lxlfw_blobs(argc, argv);
955 else if (!strcmp(argv[1], "create"))
956 return lxlfw_create(argc, argv);
957 else if (!strcmp(argv[1], "insert"))
958 return lxlfw_insert(argc, argv);
959 }
960
961 usage();
962 return 0;
963 }