2 * make_fw.c - iPodLinux loader installer
4 * Copyright (C) 2003 Daniel Palffy
6 * based on Bernard Leach's patch_fw.c
7 * Copyright (C) 2002 Bernard Leach
8 * big endian support added 2003 Steven Lucy
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
37 /* Some firmwares have padding becore the actual image. */
38 #define IMAGE_PADDING ((fw_version == 3) ? 0x200 : 0)
39 #define FIRST_OFFSET (TBL + 0x200 + IMAGE_PADDING)
42 unsigned short fw_version
= 2;
44 typedef struct _image
{
45 char type
[4]; /* '' */
47 char pad1
[4]; /* 0000 0000 */
48 unsigned devOffset
; /* byte offset of start of image code */
49 unsigned len
; /* length in bytes of image */
50 unsigned addr
; /* load address */
51 unsigned entryOffset
; /* execution start within image */
52 unsigned chksum
; /* checksum for image */
53 unsigned vers
; /* image version */
54 unsigned loadAddr
; /* load address for image */
57 static char *apple_copyright
= "{{~~ /-----\\ {{~~ / \\ {{~~| | {{~~| S T O P | {{~~| | {{~~ \\ / {{~~ \\-----/ Copyright(C) 2001 Apple Computer, Inc.---------------------------------------------------------------------------------------------------------";
63 return ((l
& 0xff) << 24)
65 | ((l
& 0xff0000) >> 8)
66 | ((l
& 0xff000000) >> 24);
71 switch_16(unsigned short s
)
74 return ((s
& 0xff) << 8) | ((s
& 0xff00) >> 8);
81 switch_endian(image_t
*image
)
84 image
->id
= switch_32(image
->id
);
85 image
->devOffset
= switch_32(image
->devOffset
);
86 image
->len
= switch_32(image
->len
);
87 image
->addr
= switch_32(image
->addr
);
88 image
->entryOffset
= switch_32(image
->entryOffset
);
89 image
->chksum
= switch_32(image
->chksum
);
90 image
->vers
= switch_32(image
->vers
);
91 image
->loadAddr
= switch_32(image
->loadAddr
);
96 print_image(image_t
*image
, const char *head
)
98 printf("%stype: '%s' id: 0x%4x len: 0x%4x addr: 0x%4x vers: 0x%8x\r\n",
99 head
, image
->type
, image
->id
, image
->len
, image
->addr
, image
->vers
);
100 printf("devOffset: 0x%08X entryOffset: 0x%08X "
101 "loadAddr: 0x%08X chksum: 0x%08X\n",
102 image
->devOffset
, image
->entryOffset
,
103 image
->loadAddr
, image
->chksum
);
109 printf("Usage: patch_fw [-h]\n"
110 " patch_fw [-v] -o outfile -e img_no fw_file\n"
111 " patch_fw [-v] -g gen [-r rev] -o outfile [-i img_from_-e]* [-l raw_img]* ldr_img\n\n"
112 " -g: set target ipod generation, valid options are: 1g, 2g, 3g\n"
113 " 4g, 5g, scroll, touch, dock, mini, photo, color, nano and video\n"
114 " -e: extract the image at img_no in boot table to outfile\n"
115 " fw_file is an original firmware image\n"
116 " the original firmware has the sw at 0, and a flash updater at 1\n"
117 " -i|-l: create new image to outfile\n"
118 " up to 5 images, any of -i or -l allowed\n"
119 " -i: image extracted with -e, load and entry address preserved\n"
120 " -l: raw image, loaded to 0x28000000, entry at 0x00000000\n"
121 " -r: set master revision to rev (for example 210 for 2.10)\n"
122 " may be needed if newest -e img is not the same as the flash rev\n"
123 " ldr_img is the iPodLinux loader binary.\n"
124 " first image is loaded by default, 2., 3., 4. or 5. loaded if\n"
125 " rew, menu, play or ff is hold while booting\n\n"
127 " This program is used to create a bootable ipod image.\n\n");
130 /* read len bytes from the beginning of s,
131 * calculate checksum, and
132 * if (d) copy to current offset in d */
134 copysum(FILE *s
, FILE *d
, unsigned len
, unsigned off
)
139 if (fseek(s
, off
, SEEK_SET
) == -1) {
140 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
143 for (i
= 0; i
< len
; i
++) {
144 if (fread(&temp
, 1, 1, s
) != 1) {
145 fprintf(stderr
, "fread failed: %s\n", strerror(errno
));
148 sum
= sum
+ (temp
& 0xff);
150 if (fwrite(&temp
, 1, 1, d
) != 1) {
151 fprintf(stderr
, "fwrite failed: %s\n", strerror(errno
));
158 /* load the boot entry from
159 * boot table at offset,
163 load_entry(image_t
*image
, FILE *fw
, unsigned offset
, int entry
)
165 if (fseek(fw
, offset
+ entry
* sizeof(image_t
), SEEK_SET
) == -1) {
166 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
169 if (fread(image
, sizeof(image_t
), 1, fw
) != 1) {
170 fprintf(stderr
, "fread failed: %s\n", strerror(errno
));
173 switch_endian(image
);
177 /* store the boot entry to
178 * boot table at offset,
182 write_entry(image_t
*image
, FILE *fw
, unsigned offset
, int entry
)
184 if (fseek(fw
, offset
+ entry
* sizeof(image_t
), SEEK_SET
) == -1) {
185 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
188 switch_endian(image
);
189 if (fwrite(image
, sizeof(image_t
), 1, fw
) != 1) {
190 fprintf(stderr
, "fwrite failed: %s\n", strerror(errno
));
191 switch_endian(image
);
194 switch_endian(image
);
198 /* extract a single image from the fw
199 * the first 40 bytes contain a boot table entry,
200 * padded to one block (512 bytes */
202 extract(FILE *f
, int idx
, FILE *out
)
205 unsigned char buf
[512];
208 fseek(f
, 0x100 + 10, SEEK_SET
);
209 fread(&fw_version
, sizeof(fw_version
), 1, f
);
210 fw_version
= switch_16(fw_version
);
212 image
= (image_t
*)buf
;
213 if (load_entry(image
, f
, TBL
, idx
) == -1)
215 off
= image
->devOffset
+ IMAGE_PADDING
;
217 if (fseek(f
, off
, SEEK_SET
) == -1) {
218 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
221 if (write_entry(image
, out
, 0x0, 0) == -1)
223 if (fseek(out
, 512, SEEK_SET
) == -1) {
224 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
227 if (copysum(f
, out
, image
->len
, off
) == -1)
233 /* return the size of f */
239 if (fseek(f
, 0, SEEK_END
) == -1) {
240 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
243 if ((ret
= ftell(f
)) == -1) {
244 fprintf(stderr
, "ftell failed: %s\n", strerror(errno
));
253 char ch
[4] = { '\0', '\1', '\2', '\3' };
254 unsigned i
= 0x00010203;
256 if (*((int *)ch
) == i
)
264 main(int argc
, char **argv
)
267 int verbose
= 0, i
, ext
= 0;
268 FILE *in
= NULL
, *out
= NULL
;
272 { '!', 'A', 'T', 'A' }, // magic
274 { '\0', '\0', '\0', '\0' }, // pad
281 0xffffffff // loadAddr
284 unsigned version
= 0, offset
= 0, len
= 0;
291 while ((c
= getopt(argc
, argv
, "3hve:o:i:l:r:g:")) != -1)
294 if (verbose
|| in
|| out
|| images_done
|| ext
) {
296 "-[?h] is exclusive with other arguments\n");
304 fprintf(stderr
, "Warning: multiple -v options specified\n");
309 image
.addr
= 0x10000000;
313 if ((strcasecmp(optarg
, "4g") == 0) ||
314 (strcasecmp(optarg
, "mini") == 0) ||
315 (strcasecmp(optarg
, "nano") == 0) ||
316 (strcasecmp(optarg
, "photo") == 0) ||
317 (strcasecmp(optarg
, "color") == 0) ||
318 (strcasecmp(optarg
, "video") == 0) ||
319 (strcasecmp(optarg
, "5g") == 0)) {
321 image
.addr
= 0x10000000;
322 if ((strcasecmp(optarg
, "5g") == 0) || (strcasecmp(optarg
, "video") == 0)) {
326 else if ((strcasecmp(optarg
, "1g") != 0) &&
327 (strcasecmp(optarg
, "2g") != 0) &&
328 (strcasecmp(optarg
, "3g") != 0) &&
329 (strcasecmp(optarg
, "scroll") != 0) &&
330 (strcasecmp(optarg
, "touch") != 0) &&
331 (strcasecmp(optarg
, "dock") != 0)) {
332 fprintf(stderr
, "%s: bad gen. Valid options are: 1g, 2g,"
333 " 3g, 4g, 5g, scroll, touch, dock, mini, nano,"
334 " photo, color, and video\n", optarg
);
340 fprintf(stderr
, "output already opened\n");
344 if ((out
= fopen(optarg
, "wb+")) == NULL
) {
345 fprintf(stderr
, "Cannot open output file %s\n", optarg
);
350 if (!out
|| images_done
|| ext
) {
355 ext
= atoi(optarg
) + 1;
362 if (images_done
== 5) {
363 fprintf(stderr
, "Only 5 images supported\n");
366 if ((in
= fopen(optarg
, "rb")) == NULL
) {
367 fprintf(stderr
, "Cannot open firmware image file %s\n", optarg
);
370 if (load_entry(images
+ images_done
, in
, 0, 0) == -1)
372 if (!offset
) offset
= FIRST_OFFSET
;
373 else offset
= (offset
+ 0x1ff) & ~0x1ff;
374 images
[images_done
].devOffset
= offset
;
375 if (fseek(out
, offset
, SEEK_SET
) == -1) {
376 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
379 if ((images
[images_done
].chksum
= copysum(in
, out
,
380 images
[images_done
].len
, 0x200)) == -1)
382 offset
+= images
[images_done
].len
;
383 if (verbose
) print_image(images
+ images_done
, "Apple image added: ");
392 if (images_done
== 5) {
393 fprintf(stderr
, "Only 5 images supported\n");
396 if ((in
= fopen(optarg
, "rb")) == NULL
) {
397 fprintf(stderr
, "Cannot open linux image file %s\n", optarg
);
400 if (!offset
) offset
= FIRST_OFFSET
;
401 else offset
= (offset
+ 0x1ff) & ~0x1ff;
402 images
[images_done
] = image
;
403 images
[images_done
].devOffset
= offset
;
404 if ((images
[images_done
].len
= lengthof(in
)) == -1)
406 if (fseek(out
, offset
, SEEK_SET
) == -1) {
407 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
410 if ((images
[images_done
].chksum
= copysum(in
, out
,
411 images
[images_done
].len
, 0)) == -1)
413 offset
+= images
[images_done
].len
;
414 if (verbose
) print_image(images
+ images_done
, "Linux image added: ");
423 version
= strtol(optarg
, NULL
, 16);
426 fprintf(stderr
, "invalid option -%c specified\n", optopt
);
430 fprintf(stderr
, "option -%c needs an argument\n", optopt
);
435 if (argc
- optind
!= 1) {
441 if ((in
= fopen(argv
[optind
], "rb")) == NULL
) {
442 fprintf(stderr
, "Cannot open firmware image file %s\n", argv
[optind
]);
445 if (extract(in
, ext
-1, out
) == -1) return 1;
446 fclose(in
); fclose(out
);
454 printf("Generating firmware image compatible with ");
455 if (fw_version
== 3) {
457 printf("iPod video\n");
459 printf("iPod mini, 4g and iPod photo/color...\n");
462 printf("1g, 2g and 3g iPods...\n");
466 fprintf(stderr
, "no images specified!\n");
469 if ((in
= fopen(argv
[optind
], "rb")) == NULL
) {
470 fprintf(stderr
, "Cannot open loader image file %s\n", argv
[optind
]);
473 offset
= (offset
+ 0x1ff) & ~0x1ff;
474 if ((len
= lengthof(in
)) == -1)
476 if (fseek(out
, offset
, SEEK_SET
) == -1) {
477 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
480 if (copysum(in
, out
, len
, 0) == -1)
482 for (i
=0; i
< images_done
; i
++) {
483 if (images
[i
].vers
> image
.vers
) image
.vers
= images
[i
].vers
;
484 if (write_entry(images
+i
, out
, offset
+0x0100, i
) == -1)
487 if (version
) image
.vers
= version
;
488 image
.len
= offset
+ len
- FIRST_OFFSET
;
489 image
.entryOffset
= offset
- FIRST_OFFSET
;
490 if ((image
.chksum
= copysum(out
, NULL
, image
.len
, FIRST_OFFSET
)) == -1)
492 if (fseek(out
, 0x0, SEEK_SET
) == -1) {
493 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
496 if (fwrite(apple_copyright
, 0x100, 1, out
) != 1) {
497 fprintf(stderr
, "fwrite failed: %s\n", strerror(errno
));
500 version
= switch_32(0x5b68695d); /* magic */
501 if (fwrite(&version
, 4, 1, out
) != 1) {
502 fprintf(stderr
, "fwrite failed: %s\n", strerror(errno
));
505 version
= switch_32(0x00004000); /* magic */
506 if (fwrite(&version
, 4, 1, out
) != 1) {
507 fprintf(stderr
, "fwrite failed: %s\n", strerror(errno
));
510 if (fw_version
== 3) {
511 version
= switch_32(0x0003010c); /* magic */
513 version
= switch_32(0x0002010c); /* magic */
516 if (fwrite(&version
, 4, 1, out
) != 1) {
517 fprintf(stderr
, "fwrite failed: %s\n", strerror(errno
));
520 if (write_entry(&image
, out
, TBL
, 0) == -1)
522 if (verbose
) print_image(&image
, "Master image: ");
527 if ((in
= fopen("apple_sw_5g_rcsc.bin", "rb")) == NULL
) {
528 fprintf(stderr
, "Cannot open firmware image file %s\n", "apple_sw_5g_rcsc.bin");
531 if (load_entry(&rsrc
, in
, 0, 0) == -1) {
534 rsrc
.devOffset
= image
.devOffset
+ image
.len
;
535 rsrc
.devOffset
= ((rsrc
.devOffset
+ 0x1ff) & ~0x1ff) + 0x200;
536 if (fseek(out
, rsrc
.devOffset
+ IMAGE_PADDING
, SEEK_SET
) == -1) {
537 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
540 if ((rsrc
.chksum
= copysum(in
, out
, rsrc
.len
, 0x200)) == -1) {
545 if (write_entry(&rsrc
, out
, TBL
, 1) == -1) {
549 if (verbose
) print_image(&rsrc
, "rsrc image: ");