-
Notifications
You must be signed in to change notification settings - Fork 0
/
make_fw2.c
1383 lines (1224 loc) · 44.3 KB
/
make_fw2.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* make_fw.c - iPodLinux loader installer
*
* Copyright (C) 2006 Joshua Oreman
*
* based on Danial Palffy's make_fw.c
* Copyright (C) 2003 Daniel Palffy
*
* based on Bernard Leach's patch_fw.c
* Copyright (C) 2002 Bernard Leach
* big endian support added 2003 Steven Lucy
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* NOTE:
This is known non-working on 5.5g iPods.
The firmware partition format has changed slightly. On the
5g, there was 512-bytes of padding before the firmware
image. On the 5.5g this is now 2048 bytes. i.e. one
logical sector. This will mean make_fw etc will need
adapting.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <ctype.h>
#include <setjmp.h>
#include "make_fw2.h"
#ifdef __WIN32__
#include "getopt.c"
#else
#include <getopt.h>
#endif
#define TBL 0x4200
#define IMAGE_PADDING ((fw_version == 3)? 0x200 : 0)
#define FIRST_OFFSET (TBL + 0x200 + IMAGE_PADDING)
static int fw_version = 0;
static int image_version = 0;
static int verbose = 1;
static int generation = 0;
static int loadertype = 1;
static int loadall = 0;
static const char *output_file = 0;
static struct imginf *images = 0;
static int num_images = 0;
jmp_buf fw_error_out;
#define ERROR_EXIT(n) longjmp(fw_error_out,n)
static int big_endian = 0;
int stdio_open (fw_fileops *fo, const char *name, int writing)
{
fo->data = fopen (name, writing? "w+b" : "rb");
if (!fo->data) return -1;
return 0;
}
void stdio_close (fw_fileops *fo) { fclose ((FILE *)fo->data); }
int stdio_read (struct fops *fo, void *buf, int len) { return fread (buf, 1, len, (FILE *)fo->data); }
int stdio_write (struct fops *fo, const void *buf, int len) { return fwrite (buf, 1, len, (FILE *)fo->data); }
int stdio_lseek (struct fops *fo, long long off, int whence) { return fseek ((FILE *)fo->data, off, whence); }
long long stdio_tell (struct fops *fo) { return ftell ((FILE *)fo->data); }
fw_fileops fw_default_fileops = { stdio_open, stdio_close, stdio_read, stdio_write, stdio_lseek, stdio_tell, 0 };
static const char *apple_copyright = "{{~~ /-----\\ "
"{{~~ / \\ "
"{{~~| | "
"{{~~| S T O P | "
"{{~~| | "
"{{~~ \\ / "
"{{~~ \\-----/ "
"Copyright(C) 2001 Apple Computer, Inc."
"-----------------------------------------------------"
"----------------------------------------------------";
static void *mallocz (int nmemb, int size)
{
void *ret = malloc (nmemb * size);
memset (ret, 0, nmemb * size);
return ret;
}
static unsigned int
switch_32 (unsigned int l)
{
if (big_endian)
return ((l & 0xff) << 24) | ((l & 0xff00) << 8) | ((l & 0xff0000) >> 8) | ((l & 0xff000000) >> 24);
return l;
}
static unsigned short
switch_16 (unsigned short s)
{
if (big_endian)
return ((s & 0xff) << 8) | ((s & 0xff00) >> 8);
return s;
}
static void
switch_endian (fw_image_t *image)
{
if (big_endian) {
image->devOffset = switch_32(image->devOffset);
image->len = switch_32(image->len);
image->addr = switch_32(image->addr);
image->entryOffset = switch_32(image->entryOffset);
image->chksum = switch_32(image->chksum);
image->vers = switch_32(image->vers);
image->loadAddr = switch_32(image->loadAddr);
}
/* make the ID readable */
char idt[2];
idt[0] = image->id[0];
idt[1] = image->id[1];
image->id[0] = image->id[3];
image->id[1] = image->id[2];
image->id[2] = idt[1];
image->id[3] = idt[0];
}
void
fw_clear()
{
fw_image_info *inf = images;
int i;
while (inf) {
if (inf->hassubs) {
for (i = 0; i < inf->nsubs; i++) {
if (inf->subs[i]) {
if (inf->subs[i]->memblock) free (inf->subs[i]->memblock);
free (inf->subs[i]);
}
}
}
if (inf->memblock) free (inf->memblock);
fw_image_info *last = inf;
inf = inf->next;
free (last);
}
images = NULL;
}
static void
print_image (fw_image_t *image, const char *head)
{
if (verbose >= 2) {
printf ("%stype: `%c%c%c%c' id: `%c%c%c%c' len: 0x%08x addr: 0x%08x vers: 0x%08x\n",
head, image->type[3], image->type[2], image->type[1], image->type[0],
image->id[0], image->id[1], image->id[2], image->id[3],
image->len, image->addr, image->vers);
printf ("devOffset: 0x%08x entryOffset: 0x%08x loadAddr: 0x%08x chksum: 0x%08x\n",
image->devOffset, image->entryOffset, image->loadAddr, image->chksum);
} else if (verbose) {
printf ("%s%c%c%c%c: %d bytes loaded from +%d to 0x%08x",
head, image->id[0], image->id[1], image->id[2], image->id[3],
image->len, image->devOffset, image->addr);
if (image->entryOffset)
printf (" executing from 0x%08x", image->addr + image->entryOffset);
printf ("\n");
}
}
void
usage (int exitcode)
{
printf ("make_fw is a program used to create a bootable iPod image.\n"
"It can be used in any of the following modes; you must specify\n"
"exactly one mode.\n\n"
"This screen:\n"
" make_fw [-h]\n"
"List images in a firmware dump:\n"
" make_fw -t DUMPFILENAME\n"
"Extract an image from a firmware dump:\n"
" make_fw -x IMAGENAME DUMPFILENAME\n"
" The image will be extracted to IMAGENAME.fw, unless you\n"
" specify -o.\n"
"Create a firmware dump:\n"
" make_fw -c IMAGENAME[=IMAGEFILE] [IMAGENAME[=IMAGEFILE] [...]]\n"
" IMAGEFILEs may be either files extracted with -x or raw\n"
" binaries. The output will be written to ipodlinux.fw, unless\n"
" you specify -o.\n\n"
"Options:\n"
" -g <gen> Specify the generation of iPod for which you're creating\n"
" the firmware. This is REQUIRED. Valid values include:\n"
" 1g, 2g, 3g, 4g, 5g (scroll, touch, dock, click, video)\n"
" mini, photo, color, nano\n"
" You may also specify a raw hw_ver number preceded by `x', or\n"
" a raw firmware version number preceded by `v', if you know\n"
" what you're doing.\n"
" -1 For options where it matters, consider the image as using loader1.\n"
" -2 Ditto, for loader2.\n"
" -3 Equivalent to `-g v3', for compatibility reasons. (Creates an image\n"
" compatible with iPods 4g/mini through 5g/video inclusive.)\n"
" -A <file> With -c, put [a]ll images from the dump <file> into the output.\n"
" -a <file> Like -A, but treat the osos image like -i does instead of keeping\n"
" its name the same.\n"
" -b <ldr> For loader1, equivalent to `osos@=<ldr>'; for loader2, `osos=<ldr>'.\n"
" -d <id> Ignore the image with ID <id> when using -a, -A, -p, -P, -x, or -t.\n"
" This may be specified multiple times.\n"
" -i <file> Equivalent to `ososN=<file>', where N starts from 0 and increases\n"
" -l <file> with each -i or -l option. Files given with -i are assumed to be\n"
" Apple firmware, and with -l are assumed to be Linux kernels.\n"
" -m Load all inputs before writing any outputs. You must specify this\n"
" option if input and output are the same file. Uses lots of memory.\n"
" -n <1>=<2> Rename the image named <2> to <1>. For instance, -n aple=osos1.\n"
" Must come after the <2> image has been loaded, or results will be\n"
" unpredictable.\n"
" -o <file> Specifies where the output should go. If you don't specify this,\n"
" a sensible default is used.\n"
" -p <file> Equivalent to -m -a <file> -o <file>; [p]atch the image in.\n"
" -P <file> Equivalent to -m -A <file> -o <file>.\n"
" -r <rev> Specify the revision number, in hex, to use for the top-level images.\n"
" Use only if you know what you're doing.\n"
" -v Increase the verbosity (chattiness).\n"
" -q Decrease the verbosity.\n\n"
"The iPod's firmware partition is organized as a set of `images'; each has\n"
"a four-character ID, like `osos' or `aupd' or `rsrc'. Whenever an IMAGENAME\n"
"is specified above, it's looking for such an ID. If you put a number after\n"
"an ID, it refers to a subimage (Linux or the Apple firmware) used\n"
"by ipodloader1. If you put an @ sign after one, it refers to the loader\n"
"stub image. (See the examples.)\n\n"
"Examples:\n"
" To create a firmware dump using ipodloader1:\n"
" ./make_fw -1 -g <gen> -c -a ipod.fw -l linux.bin -b loader.bin\n"
" The same, but the `long way':\n"
" ./make_fw -g <gen> -c osos@=loader.bin osos0=ipod.fw:osos osos1=linux.bin\n");
}
#define READING 0
#define WRITING 1
fw_fileops *
fw_fops_open (const char *filename, int mode)
{
fw_fileops *ret = malloc (sizeof(fw_fileops));
memcpy (ret, &fw_default_fileops, sizeof(fw_fileops));
if (ret->open (ret, filename, mode) < 0) {
perror (filename);
ERROR_EXIT (10);
}
return ret;
}
/** Checksum functions **/
static void
updatesum (const unsigned char *buf, int len, unsigned int *sum)
{
int i;
for (i = 0; i < len; i++) {
*sum += buf[i];
}
}
#define BUFFER_SIZE 512
static unsigned int
copysum (fw_fileops *s, fw_fileops *d, unsigned int len, unsigned int off)
{
unsigned char buf[BUFFER_SIZE];
unsigned int sum = 0;
unsigned int pos = 0;
if (s->lseek (s, off, SEEK_SET) < 0) {
fprintf (stderr, "seek failed: %s\n", strerror (errno));
ERROR_EXIT (10);
}
while (pos < len) {
int rdlen = (pos + BUFFER_SIZE < len)? BUFFER_SIZE : len - pos;
int rret;
if ((rret = s->read (s, buf, rdlen)) != rdlen) {
if (rret >= 0) {
fprintf (stderr, "short read on file\n");
ERROR_EXIT (10);
} else {
fprintf (stderr, "read failed: %s\n", strerror (errno));
ERROR_EXIT (10);
}
}
updatesum (buf, rdlen, &sum);
if (d) {
if (d->write (d, buf, rdlen) != rdlen) {
fprintf (stderr, "write failed: %s\n", strerror (errno));
ERROR_EXIT (10);
}
}
pos += BUFFER_SIZE;
}
return sum;
}
static unsigned int
writesum (const unsigned char *s, fw_fileops *d, unsigned int len)
{
unsigned int sum = 0;
updatesum (s, len, &sum);
if (d->write (d, s, len) != (int)len) {
fprintf (stderr, "write failed: %s\n", strerror (errno));
ERROR_EXIT (10);
}
return sum;
}
/* return the size of f */
static unsigned int
lengthof(fw_fileops *f)
{
if (f->lseek(f, 0, SEEK_END) < 0) {
fprintf(stderr, "seek failed: %s\n", strerror(errno));
return -1;
}
return f->tell(f);
}
void
fw_set_options (int opts)
{
loadall = !!(opts & FW_LOAD_IMAGES_TO_MEMORY);
switch (opts & (FW_LOADER1|FW_LOADER2)) {
case FW_LOADER1:
loadertype = 1;
break;
case FW_LOADER2:
loadertype = 2;
break;
default:
fprintf (stderr, "Warning: invalid loader type option passed to fw_set_options()\n");
break;
}
verbose += !!(opts & FW_VERBOSE);
verbose -= !!(opts & FW_QUIET);
}
void
fw_test_endian(void)
{
char ch[4] = { '\0', '\1', '\2', '\3' };
unsigned i = 0x00010203;
if (*((unsigned *)ch) == i)
big_endian = 1;
else
big_endian = 0;
return;
}
struct idlist
{
const char *name;
struct idlist *next;
} *to_extract = 0, *to_ignore = 0;
static void
add_id (struct idlist **head, const char *name)
{
struct idlist *current = *head;
if (!current)
current = *head = (struct idlist *)malloc (sizeof(struct idlist));
else {
while (current->next) current = current->next;
current = current->next = (struct idlist *)malloc (sizeof(struct idlist));
}
current->name = name;
current->next = 0;
}
void fw_clear_extract() { to_extract = 0; }
void fw_clear_ignore() { to_ignore = 0; }
void fw_add_extract (const char *name) { add_id (&to_extract, name); }
void fw_add_ignore (const char *name) { add_id (&to_ignore, name); }
static int
find_id (struct idlist *head, const char *name)
{
struct idlist *current = head;
while (current) {
if (!strcmp (current->name, name))
return 1;
current = current->next;
}
return 0;
}
/* load the boot entry from
* boot table at offset,
* entry number entry
* file fw */
static int
load_entry(fw_image_t *image, fw_fileops *fw, unsigned offset, int entry)
{
if (fw->lseek(fw, offset + entry * sizeof(fw_image_t), SEEK_SET) < 0) {
fprintf(stderr, "fseek failed: %s\n", strerror(errno));
return -1;
}
if (fw->read(fw, image, sizeof(fw_image_t)) != sizeof(fw_image_t)) {
fprintf(stderr, "fread failed: %s\n", strerror(errno));
return -1;
}
switch_endian(image);
return 0;
}
/* store the boot entry to
* boot table at offset,
* entry number entry
* file fw */
static int
write_entry(fw_image_t *image, fw_fileops *fw, unsigned offset, int entry, int pad)
{
if (fw->lseek(fw, offset + entry * sizeof(fw_image_t), SEEK_SET) < 0) {
fprintf(stderr, "fseek failed: %s\n", strerror(errno));
return -1;
}
if (pad) image->devOffset -= IMAGE_PADDING;
switch_endian(image);
if (fw->write(fw, image, sizeof(fw_image_t)) != sizeof(fw_image_t)) {
fprintf(stderr, "fwrite failed: %s\n", strerror(errno));
switch_endian(image);
return -1;
}
switch_endian(image);
if (pad) image->devOffset += IMAGE_PADDING;
return 0;
}
/* Writes the image described by inf to the file out, offset
* inf->header.devOffset and length inf->header.len; computes
* the checksum and updates inf->header.checksum with it;
* then writes the image header to the boot table at offset,
* slot entry.
*/
static int
write_image (fw_image_info *inf, fw_fileops *out, unsigned offset, int entry, int pad)
{
if (out->lseek (out, inf->header.devOffset, SEEK_SET) < 0) {
fprintf (stderr, "fseek failed: %s\n", strerror (errno));
return -1;
}
if (loadall && inf->memblock) {
inf->header.chksum = writesum (inf->memblock, out, inf->header.len);
} else {
inf->header.chksum = copysum (inf->file, out, inf->header.len, inf->fileoff);
}
return write_entry (&inf->header, out, offset, entry, pad);
}
/* Finds the image with the specified ID and returns it,
* or makes a new one.
* Only the first four characters of `id' are considered.
* Pass subimg = 0 to find a non-parent image, '@' to
* find a parent image, '?' for either, 'N' to make
* the next child of that id, or '0'-'4' to find/make that child,
* or 0 to find/make a non-sub image.
*/
static fw_image_info *
find_or_make_image (const char *id, char subimg)
{
/* Find the parent first. */
fw_image_info *current = images;
int found = 0;
while (current) {
if (!strncmp (id, current->name, 4)) {
found = 1;
break;
}
if (!current->next) break;
current = current->next;
}
/* Make the parent if we didn't find it. */
if (!found) {
if (current) { /* adding onto an existing list */
current = current->next = (fw_image_info *)mallocz (1, sizeof(fw_image_info));
} else { /* starting off the list */
current = images = (fw_image_info *)mallocz (1, sizeof(fw_image_info));
}
current->name = strdup (id);
memcpy (current->header.id, id, 4);
current->hassubs = (subimg != 0);
if (current->hassubs) { current->name[4] = '@'; current->name[5] = 0; }
}
/* Find the appropriate child. */
switch (subimg) {
case 0: /* We just want a parent, so we're done! */
case '?':
if (current->hassubs && (subimg != '?')) {
fprintf (stderr, "warning: overriding %s with non-parental version %s\n", current->name, id);
current->name = strdup (id);
current->hassubs = 0;
}
return current;
case '@': /* Similar. */
if (!current->hassubs) {
fprintf (stderr, "warning: overriding %s with parental version %s\n", current->name, id);
current->name = strdup (id);
current->hassubs = 1;
}
return current;
case 'N': /* Find the next sub. */
if (current->nsubs >= 5) {
fprintf (stderr, "error: too many sub-images for %s\n", current->name);
ERROR_EXIT (11);
}
subimg = current->nsubs + '0';
/* FALLTHRU */
case '0': case '1': case '2': case '3': case '4':
if (!current->hassubs) {
fprintf (stderr, "error: can't add sub-image %s to a non-parental parent %s\n", id, current->name);
ERROR_EXIT (11);
}
int subnr = subimg - '0';
if (current->nsubs <= subnr)
current->nsubs = subnr + 1;
if (!current->subs[subnr]) {
current->subs[subnr] = (fw_image_info *)mallocz (1, sizeof(fw_image_info));
current->subs[subnr]->name = strdup (id);
}
return current->subs[subnr];
default:
fprintf (stderr, "error: unrecognized subimage type `%c'\n", subimg);
ERROR_EXIT (11);
}
return 0;
}
void
fw_rename_image (const char *oldname, const char *newname)
{
fw_image_info *inf = find_or_make_image (oldname, '?');
if (!inf) {
fprintf (stderr, "Error: image %s does not exist\n", oldname);
ERROR_EXIT (11);
}
if (oldname[4] != newname[4]) {
if (isdigit (oldname[4]) && isdigit (newname[4])) {
int oldsub = oldname[4] - '0';
int newsub = newname[4] - '0';
fw_image_info *tmp = inf->subs[oldsub]; // swap the subs
inf->subs[oldsub] = inf->subs[newsub];
inf->subs[newsub] = tmp;
if (newsub >= inf->nsubs) inf->nsubs = newsub + 1;
inf = inf->subs[newsub];
if (!inf) {
fprintf (stderr, "Error: image %s does not exist\n", oldname);
ERROR_EXIT (11);
}
} else if (isdigit (oldname[4]) && !isdigit (newname[4])) {
int oldsub = oldname[4] - '0';
fw_image_info *img = inf->subs[oldsub]; // take it out of the subs list
if (!img) {
fprintf (stderr, "Error: image %s does not exist\n", oldname);
ERROR_EXIT (11);
}
inf->subs[oldsub] = 0;
fw_image_info *next = inf->next;
inf->next = img; // and put it in the top one
img->next = next;
inf = img;
} else if (!isdigit (oldname[4]) && isdigit (newname[4])) {
fw_image_info *cur = images;
while (cur->next && (strncmp (cur->next->header.id, oldname, 4) != 0))
cur = cur->next;
if (!cur->next) {
fprintf (stderr, "Error: image %s does not exist\n", oldname);
ERROR_EXIT (11);
}
cur->next = cur->next->next; // take it out of the top list
int newsub = newname[4] - '0';
fw_image_info *topimg = find_or_make_image (newname, '@');
topimg->subs[newsub] = inf; // and put it in the sub one
if (newsub >= topimg->nsubs) topimg->nsubs = newsub + 1;
inf = topimg->subs[newsub];
}
}
strncpy (inf->header.id, newname, 4);
if (inf->name) free (inf->name);
inf->name = strdup (newname);
inf->hassubs = (newname[4] == '@');
inf->header.isparent = inf->hassubs;
}
/* Adds `image' to the list of images to write, with id `id' (which may
* include things like osos1, osos@, ososN) from file `file' at offset
* image->devOffset and length image->len.
*
* Every field of `image' must be properly filled out, except the checksum.
*/
void
fw_add_image (fw_image_t *image, const char *name, const char *file)
{
if (strlen (name) < 4 || strlen (name) > 5) {
fprintf (stderr, "`%s' is an invalid ID (must be 4 or 5 characters)\n", name);
ERROR_EXIT (11);
}
if (strlen (name) == 5 && !strpbrk (name + 4, "01234N@")) {
fprintf (stderr, "`%s' is an invalid ID for a subimage (last char must be 0-4, N, or @)\n", name);
ERROR_EXIT (11);
}
fw_image_info *inf = find_or_make_image (name, name[4]);
if (inf->file) {
fprintf (stderr, "warning: overriding image %s; loading from %s +%d instead of old file +%d\n",
inf->name, file, image->devOffset, inf->fileoff);
inf->file->close (inf->file);
if (inf->memblock) free (inf->memblock);
}
inf->file = fw_fops_open (file, READING);
inf->fileoff = image->devOffset;
memcpy (&inf->header, image, sizeof(fw_image_t));
strncpy (inf->header.id, name, 4);
if (loadall) {
inf->memblock = malloc (inf->header.len);
if (!inf->memblock) {
fprintf (stderr, "error: out of memory allocating %d bytes for loading of %s; try not using -m\n",
inf->header.len, inf->name);
ERROR_EXIT (255);
}
inf->file->lseek (inf->file, inf->fileoff, SEEK_SET);
if (inf->file->read (inf->file, inf->memblock, inf->header.len) != (int)inf->header.len) {
fprintf (stderr, "error reading %d bytes from %s\n", inf->header.len, file);
ERROR_EXIT (10);
}
}
num_images++;
}
/* Finds a loaded image and returns it, or NULL if it doesn't exist. */
fw_image_info *
fw_find_image (const char *name)
{
fw_image_info *cur = images;
while (cur) {
if (!strcmp (cur->name, name)) return cur;
if (cur->hassubs) {
int i;
for (i = 0; i < cur->nsubs; i++) {
if (cur->subs[i] && !strcmp (cur->name, cur->subs[i]->name)) return cur->subs[i];
}
}
cur = cur->next;
}
return 0;
}
/* Does something with each image in `filename'. */
void
fw_iterate_images (const char *filename, void *data, void (*fn)(fw_image_t *, const char *, const char *, void *))
{
fw_fileops *in = fw_fops_open (filename, READING);
int old_version = fw_version;
unsigned short ver;
int offset = 0;
char buf[6] = "????";
fw_image_t image;
read_version:
in->lseek (in, offset + 0x10A, SEEK_SET); /* seek to the version */
in->read (in, &ver, 2);
fw_version = switch_16 (ver);
if (fw_version != 2 && fw_version != 3 && offset < 131072) {
if (offset == 0)
fprintf (stderr, "%s: no image at offset %d, looking at offset ", filename, offset);
fprintf (stderr, "%d... ", offset + 512);
offset += 512;
goto read_version;
}
if (verbose >= 2) printf ("Version: %d\n", fw_version);
if (fw_version < 2 || fw_version > 3) {
if (offset != 0)
fprintf (stderr, "nothing found\n");
fprintf (stderr, "%s: invalid version (0x%04x); are you sure that's a dump file?\n", filename, fw_version);
ERROR_EXIT (10);
}
if (offset != 0)
fprintf (stderr, "found\n");
int idx;
for (idx = 0; idx < 10; idx++) {
if (load_entry (&image, in, offset + TBL, idx) == -1)
break;
image.devOffset += offset + IMAGE_PADDING;
if (!isalnum (image.id[0]) || !isalnum (image.id[1]) || !isalnum (image.id[2]) ||
!isalnum (image.id[3]))
break;
if (image.isparent) {
int subidx;
fw_image_t subimg;
strncpy (buf, image.id, 4);
buf[4] = '@';
buf[5] = 0;
fw_image_t bootimg = image;
bootimg.devOffset += bootimg.entryOffset;
bootimg.len -= bootimg.entryOffset;
bootimg.entryOffset = 0;
if (!find_id (to_ignore, buf))
(*fn)(&bootimg, buf, filename, data);
for (subidx = 0; subidx < 5; subidx++) {
if (load_entry (&subimg, in, image.devOffset + image.entryOffset + 0x100, subidx) == -1)
break;
if (!isalnum (subimg.id[0]) || !isalnum (subimg.id[1]) || !isalnum (subimg.id[2]) ||
!isalnum (subimg.id[3]))
break;
buf[4] = subidx + '0';
if (!find_id (to_ignore, buf))
(*fn)(&subimg, buf, filename, data);
}
} else {
strncpy (buf, image.id, 4);
buf[4] = 0;
if (!find_id (to_ignore, buf))
(*fn)(&image, buf, filename, data);
}
}
if (!idx) {
fprintf (stderr, "%s: warning: no valid images\n", filename);
}
fw_version = old_version;
in->close (in);
}
/* Callback for load_all. */
static void
load_one (fw_image_t *image, const char *id, const char *filename, void *data)
{
const char *osos_replace = (const char *)data;
fw_add_image (image, (!strcmp (id, "osos"))? osos_replace : id, filename);
if (verbose >= 2) print_image (image, "Loaded image header from firmware file: ");
}
/* Loads all images from `filename', renaming the osos image
* to `osos_replace' (just pass "osos" if you don't want renaming).
*/
void
fw_load_all (const char *filename, const char *osos_replace)
{
fw_iterate_images (filename, (void *)osos_replace, load_one);
}
/* Loads a loader-dumped image from `filename'. The image's ID is taken
* to be the ID in `filename', or `osos_replace' if that id is "osos".
* The ID is changed to `newid' if `newid' is non-NULL.
*/
void
fw_load_dumped (const char *filename, const char *osos_replace, const char *newid)
{
fw_fileops *in = fw_fops_open (filename, READING);
char magic[9] = "????????";
fw_image_t image;
in->lseek (in, 64, SEEK_SET); /* seek to the magic */
in->read (in, magic, 8); /* read magic */
if (strcmp (magic, "make_fw2") != 0) {
fprintf (stderr, "%s: warning: bad magic, ignoring\n", filename);
in->close (in);
return;
}
if (load_entry (&image, in, 0, 0) == -1) {
fprintf (stderr, "%s: warning: error loading image header\n", filename);
return;
}
if (!isalnum (image.id[0]) || !isalnum (image.id[1]) || !isalnum (image.id[2]) ||
!isalnum (image.id[3])) {
fprintf (stderr, "%s: warning: invalid image ID `%c%c%c%c'\n", filename,
image.id[0], image.id[1], image.id[2], image.id[3]);
return;
}
if (!strcmp (image.id, "osos")) {
newid = osos_replace;
strncpy (image.id, newid, 4);
}
if (verbose >= 2) print_image (&image, "Loaded previously dumped image: ");
char id[5] = "????";
strncpy (id, image.id, 4);
fw_add_image (&image, newid? newid : id, filename);
}
/* Loads a raw binary from `filename' for turning into an image.
* You must appropriately specify the ID.
* The image will be loaded to 0x10000000 with its entry point at its beginning.
*/
void
fw_load_binary (const char *filename, const char *id)
{
fw_fileops *in = fw_fops_open (filename, READING);
fw_image_t image;
image.len = lengthof (in);
image.devOffset = 0;
if (generation >= 4)
image.addr = 0x10000000;
else
image.addr = 0x28000000;
image.entryOffset = 0;
image.vers = image_version;
image.loadAddr = 0xffffffff;
memcpy (image.type, "!ATA", 4);
memcpy (image.id, id, 4);
memcpy (image.pad1, "\0\0\0\0", 4);
if (verbose >= 2) print_image (&image, "Created image from raw binary file: ");
fw_add_image (&image, id, filename);
in->close (in);
}
/* The DWIMmy function. Checks magic in `filename', and executes
* either load_binary or load_dumped as appropriate.
* The ID of the loaded file is set to `id'. If `id' is NULL,
* an attempt will be made to determine whether `filename' is a
* dump, loader, or kernel, and the ID will be set appropriately.
* If `filename' is NULL, a reasonable default will be used.
*/
void
fw_load_unknown (const char *id, const char *filename)
{
char buf[128];
int idforce = !!id;
enum { Binary, Dump } type = Binary;
if (id && !id[0]) id = 0;
if (filename && !filename[0]) filename = 0;
if (!id && !filename) {
fprintf (stderr, "You must specify at least one of ID and filename.\n");
ERROR_EXIT (10);
}
if (!filename) {
sprintf (buf, "%s.fw", id);
filename = buf;
}
fw_fileops *in = fw_fops_open (filename, READING);
char magic[9] = "????????";
in->lseek (in, 64, SEEK_SET); /* seek to the magic */
in->read (in, magic, 8); /* read magic */
if (!strcmp (magic, "make_fw2")) {
type = Dump;
if (!id) id = "ososN";
if (verbose >= 2) printf ("%s: seems to be a DUMP\n", filename);
} else if (id) {
type = Binary;
} else {
/* Read the first instruction to try to figure out what it is. */
in->lseek (in, 0, SEEK_SET);
in->read (in, magic, 4);
if (!memcmp (magic, "\xff\x04\xa0\xe3", 4)) { /* mov r0, #0xff000000 @ the loader */
type = Binary;
id = (loadertype == 2)? "osos" : "osos@";
if (verbose >= 2) printf ("%s: seems to be a LOADER\n", filename);
} else if (!memcmp (magic, "\xfe\x1f\x00\xea", 4)) { /* b +0x7ffc (to 0x8000) @ the kernel */
type = Binary;
id = (loadertype == 2)? "linx" : "ososN";
if (verbose >= 2) printf ("%s: seems to be a KERNEL\n", filename);
} else {
fprintf (stderr, "%s: can't figure out whether it's a loader or kernel.", filename);
fprintf (stderr, "%s: please specify an ID, or use -l or -b\n", filename);
return;
}
}
if (type == Dump) {
if (idforce)
fw_load_dumped (filename, "osos", id);
else
fw_load_dumped (filename, id, 0);
} else {
fw_load_binary (filename, id);
}
}
/* Callback for list_images. */
static void
list_one_image (fw_image_t *image, const char *id, const char *filename, void *data)
{
char buf[64];
static int imgnr = 0;
static int subnr = 0;
(void)filename, (void)data;
if (strlen (id) == 5) {
if (id[4] == '@') {
imgnr++;
subnr = 0;
sprintf (buf, "% 2d) [M] ", imgnr);
} else {
subnr++;
sprintf (buf, " % 2d) ", subnr);
}
} else {
imgnr++;
subnr = 0;
sprintf (buf, "% 2d) ", imgnr);
}
print_image (image, buf);
}
/* List all images in `file'. */
static int
list_images (const char *file)
{
fw_iterate_images (file, 0, list_one_image);
return 0;
}
/* Callback for extract_images. */
static void
mark_one_image_for_extraction (fw_image_t *image, const char *id, const char *filename, void *data)
{
(void)data;
if (!find_id (to_extract, id) && to_extract) {
if (verbose >= 2) printf ("Not loading %s for extraction - not requested\n", id);
return;
}
if (verbose >= 2) printf ("Loading %s for extraction.\n", id);
load_one (image, id, filename, (void *)"osos");
}
/* Extract one image (and its subs) */
static void
extract_one_image (fw_image_info *inf)
{
char buf[512], ext[16];
/* Figure out what to call it. */
if (output_file) {
strcpy (buf, output_file);
if (num_images > 1) {
if (strchr (buf, '.')) {
strncpy (ext, strrchr (buf, '.'), 16);
ext[15] = 0;
*strrchr (buf, '.') = 0;
} else {
ext[0] = 0;
}