Prepare new maemo release
[maemo-rb.git] / tools / ipod_fw.c
blobeefe3f7db82dbbdd8a4376c6863017cf5265ffed
1 /*
2 * make_fw.c - iPodLinux loader installer
3 *
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.
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <unistd.h>
31 #ifdef __WIN32__
32 #include "getopt.c"
33 #endif
35 #define TBL 0x4200
37 int sectorsize = 512;
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)
43 int be;
44 unsigned short fw_version = 2;
46 typedef struct _image {
47 char type[4]; /* '' */
48 unsigned id; /* */
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 */
57 } image_t;
59 static char *apple_copyright = "{{~~ /-----\\ {{~~ / \\ {{~~| | {{~~| S T O P | {{~~| | {{~~ \\ / {{~~ \\-----/ Copyright(C) 2001 Apple Computer, Inc.---------------------------------------------------------------------------------------------------------";
61 unsigned
62 switch_32(unsigned l)
64 if (be)
65 return ((l & 0xff) << 24)
66 | ((l & 0xff00) << 8)
67 | ((l & 0xff0000) >> 8)
68 | ((l & 0xff000000) >> 24);
69 return l;
72 unsigned short
73 switch_16(unsigned short s)
75 if (be) {
76 return ((s & 0xff) << 8) | ((s & 0xff00) >> 8);
77 } else {
78 return s;
82 void
83 switch_endian(image_t *image)
85 if (be) {
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);
97 void
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);
108 void
109 usage()
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"
128 " -v: verbose\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 */
135 unsigned
136 copysum(FILE *s, FILE *d, unsigned len, unsigned off)
138 unsigned char temp;
139 unsigned sum = 0;
140 unsigned i;
141 if (fseek(s, off, SEEK_SET) == -1) {
142 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
143 return -1;
145 for (i = 0; i < len; i++) {
146 if (fread(&temp, 1, 1, s) != 1) {
147 fprintf(stderr, "Failure in copysum: ");
148 if (ferror(s))
149 fprintf(stderr, "fread error: %s\n", strerror(errno));
150 else if (feof(s))
151 fprintf(stderr, "fread length 1 at offset %d hit EOF.\n", off);
152 return -1;
154 sum = sum + (temp & 0xff);
155 if (d)
156 if (fwrite(&temp, 1, 1, d) != 1) {
157 fprintf(stderr, "Failure in copysum; fwrite error: %s\n", strerror(errno));
158 return -1;
161 return sum;
164 /* load the boot entry from
165 * boot table at offset,
166 * entry number entry
167 * file fw */
168 int
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));
173 return -1;
175 if (fread(image, sizeof(image_t), 1, fw) != 1) {
176 if (ferror(fw))
177 fprintf(stderr, "fread error (%s), ", strerror(errno));
178 else if (feof(fw))
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");
182 return -1;
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)) {
191 sectorsize=2048;
192 fprintf(stderr,"Detected 2048-byte sectors\n");
194 return 0;
197 /* store the boot entry to
198 * boot table at offset,
199 * entry number entry
200 * file fw */
201 int
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));
206 return -1;
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);
212 return -1;
214 switch_endian(image);
215 return 0;
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 */
221 int
222 extract(FILE *f, int idx, FILE *out)
224 image_t *image;
225 unsigned char buf[512];
226 unsigned off;
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
235 entry first */
236 if (load_entry(image, f, TBL, 0) == -1)
237 return -1;
239 if (idx > 0) { /* Now read the real image (if it isn't 0) */
240 if (load_entry(image, f, TBL, idx) == -1)
241 return -1;
244 off = image->devOffset + IMAGE_PADDING;
246 if (fseek(f, off, SEEK_SET) == -1) {
247 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
248 return -1;
250 if (write_entry(image, out, 0x0, 0) == -1)
251 return -1;
252 if (fseek(out, 512, SEEK_SET) == -1) {
253 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
254 return -1;
256 if (copysum(f, out, image->len, off) == -1)
257 return -1;
259 return 0;
262 /* list all images */
263 int
264 listall(FILE *f)
266 image_t *image;
267 unsigned char buf[512];
268 int idx;
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;
276 idx = 0;
277 while (idx < 20) {
278 char prefix[32];
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);
283 ++idx;
286 return 0;
289 /* return the size of f */
290 unsigned
291 lengthof(FILE *f)
293 unsigned ret;
295 if (fseek(f, 0, SEEK_END) == -1) {
296 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
297 return -1;
299 if ((ret = ftell(f)) == -1) {
300 fprintf(stderr, "ftell failed: %s\n", strerror(errno));
301 return -1;
303 return ret;
306 void
307 test_endian(void)
309 char ch[4] = { '\0', '\1', '\2', '\3' };
310 unsigned i = 0x00010203;
312 if (*((int *)ch) == i)
313 be = 1;
314 else
315 be = 0;
316 return;
320 main(int argc, char **argv)
322 int c;
323 int verbose = 0, i, ext = 0;
324 FILE *in = NULL, *out = NULL;
325 char gen_set = 0;
326 image_t images[5];
327 image_t image = {
328 { '!', 'A', 'T', 'A' }, // magic
329 0x6f736f73, // id
330 { '\0', '\0', '\0', '\0' }, // pad
331 0x4400, // devOffset
332 0, // len
333 0x28000000, // addr
334 0, // entry
335 0, // chksum
336 0, // vers
337 0xffffffff // loadAddr
339 int images_done = 0;
340 unsigned version = 0, offset = 0, len = 0;
341 int needs_rcsc = 0;
343 test_endian();
345 /* parse options */
346 opterr = 0;
347 while ((c = getopt(argc, argv, "3hve:o:i:l:r:g:")) != -1)
348 switch (c) {
349 case 'h':
350 if (verbose || in || out || images_done || ext) {
351 fprintf(stderr,
352 "-[?h] is exclusive with other arguments\n");
353 usage();
354 return 1;
356 usage();
357 return 0;
358 case 'v':
359 if (verbose++)
360 fprintf(stderr, "Warning: multiple -v options specified\n");
361 break;
362 case '3':
363 gen_set = 1;
364 fw_version = 3;
365 image.addr = 0x10000000;
366 break;
367 case 'g':
368 gen_set = 1;
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)) {
376 fw_version = 3;
377 image.addr = 0x10000000;
378 if ((strcasecmp(optarg, "5g") == 0) || (strcasecmp(optarg, "video") == 0)) {
379 needs_rcsc = 1;
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);
391 return 1;
393 break;
394 case 'o':
395 if (out) {
396 fprintf(stderr, "output already opened\n");
397 usage();
398 return 1;
400 if ((out = fopen(optarg, "wb+")) == NULL) {
401 fprintf(stderr, "Cannot open output file %s\n", optarg);
402 return 1;
404 break;
405 case 'e':
406 if (!out || images_done || ext) {
407 usage();
408 return 1;
411 ext = atoi(optarg) + 1;
412 break;
413 case 'i':
414 if (!out || ext) {
415 usage();
416 return 1;
418 if (images_done == 5) {
419 fprintf(stderr, "Only 5 images supported\n");
420 return 1;
422 if ((in = fopen(optarg, "rb")) == NULL) {
423 fprintf(stderr, "Cannot open firmware image file %s\n", optarg);
424 return 1;
426 if (load_entry(images + images_done, in, 0, 0) == -1)
427 return 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));
433 return 1;
435 if ((images[images_done].chksum = copysum(in, out,
436 images[images_done].len, 0x200)) == -1)
437 return 1;
438 offset += images[images_done].len;
439 if (verbose) print_image(images + images_done, "Apple image added: ");
440 images_done++;
441 fclose(in);
442 break;
443 case 'l':
444 if (!out || ext) {
445 usage();
446 return 1;
448 if (images_done == 5) {
449 fprintf(stderr, "Only 5 images supported\n");
450 return 1;
452 if ((in = fopen(optarg, "rb")) == NULL) {
453 fprintf(stderr, "Cannot open linux image file %s\n", optarg);
454 return 1;
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)
461 return 1;
462 if (fseek(out, offset, SEEK_SET) == -1) {
463 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
464 return 1;
466 if ((images[images_done].chksum = copysum(in, out,
467 images[images_done].len, 0)) == -1)
468 return 1;
469 offset += images[images_done].len;
470 if (verbose) print_image(images + images_done, "Linux image added: ");
471 images_done++;
472 fclose(in);
473 break;
474 case 'r':
475 if (ext) {
476 usage();
477 return 1;
479 version = strtol(optarg, NULL, 16);
480 break;
481 case '?':
482 fprintf(stderr, "invalid option -%c specified\n", optopt);
483 usage();
484 return 1;
485 case ':':
486 fprintf(stderr, "option -%c needs an argument\n", optopt);
487 usage();
488 return 1;
491 if (argc - optind != 1) {
492 usage();
493 return 1;
496 if (ext) {
497 if ((in = fopen(argv[optind], "rb")) == NULL) {
498 fprintf(stderr, "Cannot open firmware image file %s\n", argv[optind]);
499 return 1;
501 if (extract(in, ext-1, out) == -1) return 1;
502 fclose(in); fclose(out);
503 return 0;
505 else if (!gen_set) {
506 if ((in = fopen(argv[optind], "rb")) != NULL) {
507 // just list all available entries
508 listall (in);
509 fclose(in);
510 return 0;
512 usage();
513 return 1;
516 printf("Generating firmware image compatible with ");
517 if (fw_version == 3) {
518 if (needs_rcsc) {
519 printf("iPod video\n");
520 } else {
521 printf("iPod mini, 4g and iPod photo/color...\n");
523 } else {
524 printf("1g, 2g and 3g iPods...\n");
527 if (!images_done) {
528 fprintf(stderr, "no images specified!\n");
529 return 1;
531 if ((in = fopen(argv[optind], "rb")) == NULL) {
532 fprintf(stderr, "Cannot open loader image file %s\n", argv[optind]);
533 return 1;
535 offset = (offset + 0x1ff) & ~0x1ff;
536 if ((len = lengthof(in)) == -1)
537 return 1;
538 if (fseek(out, offset, SEEK_SET) == -1) {
539 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
540 return 1;
542 if (copysum(in, out, len, 0) == -1)
543 return 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)
547 return 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)
554 return 1;
555 if (fseek(out, 0x0, SEEK_SET) == -1) {
556 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
557 return 1;
559 if (fwrite(apple_copyright, 0x100, 1, out) != 1) {
560 fprintf(stderr, "fwrite error (%s) while writing copyright\n", strerror(errno));
561 return 1;
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));
566 return 1;
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));
571 return 1;
573 if (fw_version == 3) {
574 version = switch_32(0x0003010c); /* magic */
575 } else {
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));
581 return 1;
583 if (write_entry(&image, out, TBL, 0) == -1)
584 return 1;
585 if (verbose) print_image(&image, "Master image: ");
587 if (needs_rcsc) {
588 image_t rsrc;
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");
592 return 1;
594 if (load_entry(&rsrc, in, 0, 0) == -1) {
595 return 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));
601 return 1;
603 if ((rsrc.chksum = copysum(in, out, rsrc.len, 0x200)) == -1) {
604 return 1;
606 fclose(in);
608 if (write_entry(&rsrc, out, TBL, 1) == -1) {
609 return 1;
612 if (verbose) print_image(&rsrc, "rsrc image: ");
615 return 0;