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.
39 /* Some firmwares have padding becore the actual image. */
40 #define IMAGE_PADDING ((fw_version == 3) ? sectorsize : 0)
41 #define FIRST_OFFSET (TBL + ((sectorsize == 0x200) ? 0x200 : 0x600) + IMAGE_PADDING)
44 unsigned short fw_version
= 2;
46 typedef struct _image
{
47 char type
[4]; /* '' */
49 char pad1
[4]; /* 0000 0000 */
50 unsigned devOffset
; /* byte offset of start of image code */
51 unsigned len
; /* length in bytes of image */
52 unsigned addr
; /* load address */
53 unsigned entryOffset
; /* execution start within image */
54 unsigned chksum
; /* checksum for image */
55 unsigned vers
; /* image version */
56 unsigned loadAddr
; /* load address for image */
59 static char *apple_copyright
= "{{~~ /-----\\ {{~~ / \\ {{~~| | {{~~| S T O P | {{~~| | {{~~ \\ / {{~~ \\-----/ Copyright(C) 2001 Apple Computer, Inc.---------------------------------------------------------------------------------------------------------";
65 return ((l
& 0xff) << 24)
67 | ((l
& 0xff0000) >> 8)
68 | ((l
& 0xff000000) >> 24);
73 switch_16(unsigned short s
)
76 return ((s
& 0xff) << 8) | ((s
& 0xff00) >> 8);
83 switch_endian(image_t
*image
)
86 image
->id
= switch_32(image
->id
);
87 image
->devOffset
= switch_32(image
->devOffset
);
88 image
->len
= switch_32(image
->len
);
89 image
->addr
= switch_32(image
->addr
);
90 image
->entryOffset
= switch_32(image
->entryOffset
);
91 image
->chksum
= switch_32(image
->chksum
);
92 image
->vers
= switch_32(image
->vers
);
93 image
->loadAddr
= switch_32(image
->loadAddr
);
98 print_image(image_t
*image
, const char *head
)
100 printf("%stype: '%s' id: 0x%08x len: 0x%x addr: 0x%08x vers: 0x%x\n",
101 head
, image
->type
, image
->id
, image
->len
, image
->addr
, image
->vers
);
102 printf(" devOffset: 0x%08X entryOffset: 0x%08X "
103 "loadAddr: 0x%08X chksum: 0x%08X\n",
104 image
->devOffset
, image
->entryOffset
,
105 image
->loadAddr
, image
->chksum
);
111 printf("Usage: ipod_fw [-h]\n"
112 " ipod_fw [-v] -o outfile -e img_no fw_file\n"
113 " ipod_fw [-v] -g gen [-r rev] -o outfile [-i img_from_-e]* [-l raw_img]* ldr_img\n\n"
114 " -g: set target ipod generation, valid options are: 1g, 2g, 3g\n"
115 " 4g, 5g, scroll, touch, dock, mini, photo, color, nano and video\n"
116 " -e: extract the image at img_no in boot table to outfile\n"
117 " fw_file is an original firmware image\n"
118 " the original firmware has the sw at 0, and a flash updater at 1\n"
119 " -i|-l: create new image to outfile\n"
120 " up to 5 images, any of -i or -l allowed\n"
121 " -i: image extracted with -e, load and entry address preserved\n"
122 " -l: raw image, loaded to 0x28000000, entry at 0x00000000\n"
123 " -r: set master revision to rev (for example 210 for 2.10)\n"
124 " may be needed if newest -e img is not the same as the flash rev\n"
125 " ldr_img is the iPodLinux loader binary.\n"
126 " first image is loaded by default, 2., 3., 4. or 5. loaded if\n"
127 " rew, menu, play or ff is hold while booting\n\n"
129 " This program is used to create a bootable ipod image.\n\n");
132 /* read len bytes from the beginning of s,
133 * calculate checksum, and
134 * if (d) copy to current offset in d */
136 copysum(FILE *s
, FILE *d
, unsigned len
, unsigned off
)
141 if (fseek(s
, off
, SEEK_SET
) == -1) {
142 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
145 for (i
= 0; i
< len
; i
++) {
146 if (fread(&temp
, 1, 1, s
) != 1) {
147 fprintf(stderr
, "Failure in copysum: ");
149 fprintf(stderr
, "fread error: %s\n", strerror(errno
));
151 fprintf(stderr
, "fread length 1 at offset %d hit EOF.\n", off
);
154 sum
= sum
+ (temp
& 0xff);
156 if (fwrite(&temp
, 1, 1, d
) != 1) {
157 fprintf(stderr
, "Failure in copysum; fwrite error: %s\n", strerror(errno
));
164 /* load the boot entry from
165 * boot table at offset,
169 load_entry(image_t
*image
, FILE *fw
, unsigned offset
, int entry
)
171 if (fseek(fw
, offset
+ entry
* sizeof(image_t
), SEEK_SET
) == -1) {
172 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
175 if (fread(image
, sizeof(image_t
), 1, fw
) != 1) {
177 fprintf(stderr
, "fread error (%s), ", strerror(errno
));
179 fprintf(stderr
, "fread length %lu at offset %lu hit EOF, ",
180 sizeof(image_t
), offset
+ entry
* sizeof(image_t
));
181 fprintf(stderr
, "unable to load boot entry.\n");
184 switch_endian(image
);
186 /* If we find an "osos" image with devOffset 0x4800, we have 2048-byte
187 sectors. This isn't 100% future-proof, but works as of December 2006.
188 We display this information so users can spot any false-positives that
189 may occur in the future (although this is unlikely). */
190 if ((image
->id
==0x6f736f73) && (image
->devOffset
==0x4800)) {
192 fprintf(stderr
,"Detected 2048-byte sectors\n");
197 /* store the boot entry to
198 * boot table at offset,
202 write_entry(image_t
*image
, FILE *fw
, unsigned offset
, int entry
)
204 if (fseek(fw
, offset
+ entry
* sizeof(image_t
), SEEK_SET
) == -1) {
205 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
208 switch_endian(image
);
209 if (fwrite(image
, sizeof(image_t
), 1, fw
) != 1) {
210 fprintf(stderr
, "fwrite error (%s), unable to write boot entry\n", strerror(errno
));
211 switch_endian(image
);
214 switch_endian(image
);
218 /* extract a single image from the fw
219 * the first 40 bytes contain a boot table entry,
220 * padded to one block (512 bytes */
222 extract(FILE *f
, int idx
, FILE *out
)
225 unsigned char buf
[512];
228 fseek(f
, 0x100 + 10, SEEK_SET
);
229 fread(&fw_version
, sizeof(fw_version
), 1, f
);
230 fw_version
= switch_16(fw_version
);
232 image
= (image_t
*)buf
;
234 /* We need to detect sector size, so always load image 0 directory
236 if (load_entry(image
, f
, TBL
, 0) == -1)
239 if (idx
> 0) { /* Now read the real image (if it isn't 0) */
240 if (load_entry(image
, f
, TBL
, idx
) == -1)
244 off
= image
->devOffset
+ IMAGE_PADDING
;
246 if (fseek(f
, off
, SEEK_SET
) == -1) {
247 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
250 if (write_entry(image
, out
, 0x0, 0) == -1)
252 if (fseek(out
, 512, SEEK_SET
) == -1) {
253 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
256 if (copysum(f
, out
, image
->len
, off
) == -1)
262 /* list all images */
267 unsigned char buf
[512];
270 fseek(f
, 0x100 + 10, SEEK_SET
);
271 fread(&fw_version
, sizeof(fw_version
), 1, f
);
272 fw_version
= switch_16(fw_version
);
274 image
= (image_t
*)buf
;
279 if (load_entry(image
, f
, TBL
, idx
) < 0) return -1;
280 if (!image
->id
) break;
281 sprintf (prefix
, "%2d: ", idx
);
282 print_image (image
, prefix
);
289 /* return the size of f */
295 if (fseek(f
, 0, SEEK_END
) == -1) {
296 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
299 if ((ret
= ftell(f
)) == -1) {
300 fprintf(stderr
, "ftell failed: %s\n", strerror(errno
));
309 char ch
[4] = { '\0', '\1', '\2', '\3' };
310 unsigned i
= 0x00010203;
312 if (*((int *)ch
) == i
)
320 main(int argc
, char **argv
)
323 int verbose
= 0, i
, ext
= 0;
324 FILE *in
= NULL
, *out
= NULL
;
328 { '!', 'A', 'T', 'A' }, // magic
330 { '\0', '\0', '\0', '\0' }, // pad
337 0xffffffff // loadAddr
340 unsigned version
= 0, offset
= 0, len
= 0;
347 while ((c
= getopt(argc
, argv
, "3hve:o:i:l:r:g:")) != -1)
350 if (verbose
|| in
|| out
|| images_done
|| ext
) {
352 "-[?h] is exclusive with other arguments\n");
360 fprintf(stderr
, "Warning: multiple -v options specified\n");
365 image
.addr
= 0x10000000;
369 if ((strcasecmp(optarg
, "4g") == 0) ||
370 (strcasecmp(optarg
, "mini") == 0) ||
371 (strcasecmp(optarg
, "nano") == 0) ||
372 (strcasecmp(optarg
, "photo") == 0) ||
373 (strcasecmp(optarg
, "color") == 0) ||
374 (strcasecmp(optarg
, "video") == 0) ||
375 (strcasecmp(optarg
, "5g") == 0)) {
377 image
.addr
= 0x10000000;
378 if ((strcasecmp(optarg
, "5g") == 0) || (strcasecmp(optarg
, "video") == 0)) {
382 else if ((strcasecmp(optarg
, "1g") != 0) &&
383 (strcasecmp(optarg
, "2g") != 0) &&
384 (strcasecmp(optarg
, "3g") != 0) &&
385 (strcasecmp(optarg
, "scroll") != 0) &&
386 (strcasecmp(optarg
, "touch") != 0) &&
387 (strcasecmp(optarg
, "dock") != 0)) {
388 fprintf(stderr
, "%s: bad gen. Valid options are: 1g, 2g,"
389 " 3g, 4g, 5g, scroll, touch, dock, mini, nano,"
390 " photo, color, and video\n", optarg
);
396 fprintf(stderr
, "output already opened\n");
400 if ((out
= fopen(optarg
, "wb+")) == NULL
) {
401 fprintf(stderr
, "Cannot open output file %s\n", optarg
);
406 if (!out
|| images_done
|| ext
) {
411 ext
= atoi(optarg
) + 1;
418 if (images_done
== 5) {
419 fprintf(stderr
, "Only 5 images supported\n");
422 if ((in
= fopen(optarg
, "rb")) == NULL
) {
423 fprintf(stderr
, "Cannot open firmware image file %s\n", optarg
);
426 if (load_entry(images
+ images_done
, in
, 0, 0) == -1)
428 if (!offset
) offset
= FIRST_OFFSET
;
429 else offset
= (offset
+ 0x1ff) & ~0x1ff;
430 images
[images_done
].devOffset
= offset
;
431 if (fseek(out
, offset
, SEEK_SET
) == -1) {
432 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
435 if ((images
[images_done
].chksum
= copysum(in
, out
,
436 images
[images_done
].len
, 0x200)) == -1)
438 offset
+= images
[images_done
].len
;
439 if (verbose
) print_image(images
+ images_done
, "Apple image added: ");
448 if (images_done
== 5) {
449 fprintf(stderr
, "Only 5 images supported\n");
452 if ((in
= fopen(optarg
, "rb")) == NULL
) {
453 fprintf(stderr
, "Cannot open linux image file %s\n", optarg
);
456 if (!offset
) offset
= FIRST_OFFSET
;
457 else offset
= (offset
+ 0x1ff) & ~0x1ff;
458 images
[images_done
] = image
;
459 images
[images_done
].devOffset
= offset
;
460 if ((images
[images_done
].len
= lengthof(in
)) == -1)
462 if (fseek(out
, offset
, SEEK_SET
) == -1) {
463 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
466 if ((images
[images_done
].chksum
= copysum(in
, out
,
467 images
[images_done
].len
, 0)) == -1)
469 offset
+= images
[images_done
].len
;
470 if (verbose
) print_image(images
+ images_done
, "Linux image added: ");
479 version
= strtol(optarg
, NULL
, 16);
482 fprintf(stderr
, "invalid option -%c specified\n", optopt
);
486 fprintf(stderr
, "option -%c needs an argument\n", optopt
);
491 if (argc
- optind
!= 1) {
497 if ((in
= fopen(argv
[optind
], "rb")) == NULL
) {
498 fprintf(stderr
, "Cannot open firmware image file %s\n", argv
[optind
]);
501 if (extract(in
, ext
-1, out
) == -1) return 1;
502 fclose(in
); fclose(out
);
506 if ((in
= fopen(argv
[optind
], "rb")) != NULL
) {
507 // just list all available entries
516 printf("Generating firmware image compatible with ");
517 if (fw_version
== 3) {
519 printf("iPod video\n");
521 printf("iPod mini, 4g and iPod photo/color...\n");
524 printf("1g, 2g and 3g iPods...\n");
528 fprintf(stderr
, "no images specified!\n");
531 if ((in
= fopen(argv
[optind
], "rb")) == NULL
) {
532 fprintf(stderr
, "Cannot open loader image file %s\n", argv
[optind
]);
535 offset
= (offset
+ 0x1ff) & ~0x1ff;
536 if ((len
= lengthof(in
)) == -1)
538 if (fseek(out
, offset
, SEEK_SET
) == -1) {
539 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
542 if (copysum(in
, out
, len
, 0) == -1)
544 for (i
=0; i
< images_done
; i
++) {
545 if (images
[i
].vers
> image
.vers
) image
.vers
= images
[i
].vers
;
546 if (write_entry(images
+i
, out
, offset
+0x0100, i
) == -1)
549 if (version
) image
.vers
= version
;
550 image
.len
= offset
+ len
- FIRST_OFFSET
;
551 image
.entryOffset
= offset
- FIRST_OFFSET
;
552 image
.devOffset
= (sectorsize
==512 ? 0x4400 : 0x4800);
553 if ((image
.chksum
= copysum(out
, NULL
, image
.len
, FIRST_OFFSET
)) == -1)
555 if (fseek(out
, 0x0, SEEK_SET
) == -1) {
556 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
559 if (fwrite(apple_copyright
, 0x100, 1, out
) != 1) {
560 fprintf(stderr
, "fwrite error (%s) while writing copyright\n", strerror(errno
));
563 version
= switch_32(0x5b68695d); /* magic */
564 if (fwrite(&version
, 4, 1, out
) != 1) {
565 fprintf(stderr
, "fwrite error (%s) while writing version magic\n", strerror(errno
));
568 version
= switch_32(0x00004000); /* magic */
569 if (fwrite(&version
, 4, 1, out
) != 1) {
570 fprintf(stderr
, "fwrite error (%s) while writing version magic\n", strerror(errno
));
573 if (fw_version
== 3) {
574 version
= switch_32(0x0003010c); /* magic */
576 version
= switch_32(0x0002010c); /* magic */
579 if (fwrite(&version
, 4, 1, out
) != 1) {
580 fprintf(stderr
, "fwrite error (%s) while writing version magic\n", strerror(errno
));
583 if (write_entry(&image
, out
, TBL
, 0) == -1)
585 if (verbose
) print_image(&image
, "Master image: ");
590 if ((in
= fopen("apple_sw_5g_rcsc.bin", "rb")) == NULL
) {
591 fprintf(stderr
, "Cannot open firmware image file %s\n", "apple_sw_5g_rcsc.bin");
594 if (load_entry(&rsrc
, in
, 0, 0) == -1) {
597 rsrc
.devOffset
= image
.devOffset
+ image
.len
;
598 rsrc
.devOffset
= ((rsrc
.devOffset
+ 0x1ff) & ~0x1ff) + 0x200;
599 if (fseek(out
, rsrc
.devOffset
+ IMAGE_PADDING
, SEEK_SET
) == -1) {
600 fprintf(stderr
, "fseek failed: %s\n", strerror(errno
));
603 if ((rsrc
.chksum
= copysum(in
, out
, rsrc
.len
, 0x200)) == -1) {
608 if (write_entry(&rsrc
, out
, TBL
, 1) == -1) {
612 if (verbose
) print_image(&rsrc
, "rsrc image: ");