Splash function: Fixed several bugs and problems: (1) Off-by-one bug caused a buffer...
[Rockbox.git] / tools / ipod_fw.c
blob3e5ac74470525c64fe64f253b9396d6a15e56e73
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 /* 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)
41 int be;
42 unsigned short fw_version = 2;
44 typedef struct _image {
45 char type[4]; /* '' */
46 unsigned id; /* */
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 */
55 } image_t;
57 static char *apple_copyright = "{{~~ /-----\\ {{~~ / \\ {{~~| | {{~~| S T O P | {{~~| | {{~~ \\ / {{~~ \\-----/ Copyright(C) 2001 Apple Computer, Inc.---------------------------------------------------------------------------------------------------------";
59 unsigned
60 switch_32(unsigned l)
62 if (be)
63 return ((l & 0xff) << 24)
64 | ((l & 0xff00) << 8)
65 | ((l & 0xff0000) >> 8)
66 | ((l & 0xff000000) >> 24);
67 return l;
70 unsigned short
71 switch_16(unsigned short s)
73 if (be) {
74 return ((s & 0xff) << 8) | ((s & 0xff00) >> 8);
75 } else {
76 return s;
80 void
81 switch_endian(image_t *image)
83 if (be) {
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);
95 void
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);
106 void
107 usage()
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"
126 " -v: verbose\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 */
133 unsigned
134 copysum(FILE *s, FILE *d, unsigned len, unsigned off)
136 unsigned char temp;
137 unsigned sum = 0;
138 unsigned i;
139 if (fseek(s, off, SEEK_SET) == -1) {
140 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
141 return -1;
143 for (i = 0; i < len; i++) {
144 if (fread(&temp, 1, 1, s) != 1) {
145 fprintf(stderr, "fread failed: %s\n", strerror(errno));
146 return -1;
148 sum = sum + (temp & 0xff);
149 if (d)
150 if (fwrite(&temp, 1, 1, d) != 1) {
151 fprintf(stderr, "fwrite failed: %s\n", strerror(errno));
152 return -1;
155 return sum;
158 /* load the boot entry from
159 * boot table at offset,
160 * entry number entry
161 * file fw */
162 int
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));
167 return -1;
169 if (fread(image, sizeof(image_t), 1, fw) != 1) {
170 fprintf(stderr, "fread failed: %s\n", strerror(errno));
171 return -1;
173 switch_endian(image);
174 return 0;
177 /* store the boot entry to
178 * boot table at offset,
179 * entry number entry
180 * file fw */
181 int
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));
186 return -1;
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);
192 return -1;
194 switch_endian(image);
195 return 0;
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 */
201 int
202 extract(FILE *f, int idx, FILE *out)
204 image_t *image;
205 unsigned char buf[512];
206 unsigned off;
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)
214 return -1;
215 off = image->devOffset + IMAGE_PADDING;
217 if (fseek(f, off, SEEK_SET) == -1) {
218 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
219 return -1;
221 if (write_entry(image, out, 0x0, 0) == -1)
222 return -1;
223 if (fseek(out, 512, SEEK_SET) == -1) {
224 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
225 return -1;
227 if (copysum(f, out, image->len, off) == -1)
228 return -1;
230 return 0;
233 /* return the size of f */
234 unsigned
235 lengthof(FILE *f)
237 unsigned ret;
239 if (fseek(f, 0, SEEK_END) == -1) {
240 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
241 return -1;
243 if ((ret = ftell(f)) == -1) {
244 fprintf(stderr, "ftell failed: %s\n", strerror(errno));
245 return -1;
247 return ret;
250 void
251 test_endian(void)
253 char ch[4] = { '\0', '\1', '\2', '\3' };
254 unsigned i = 0x00010203;
256 if (*((int *)ch) == i)
257 be = 1;
258 else
259 be = 0;
260 return;
264 main(int argc, char **argv)
266 int c;
267 int verbose = 0, i, ext = 0;
268 FILE *in = NULL, *out = NULL;
269 char gen_set = 0;
270 image_t images[5];
271 image_t image = {
272 { '!', 'A', 'T', 'A' }, // magic
273 0x6f736f73, // id
274 { '\0', '\0', '\0', '\0' }, // pad
275 0x4400, // devOffset
276 0, // len
277 0x28000000, // addr
278 0, // entry
279 0, // chksum
280 0, // vers
281 0xffffffff // loadAddr
283 int images_done = 0;
284 unsigned version = 0, offset = 0, len = 0;
285 int needs_rcsc = 0;
287 test_endian();
289 /* parse options */
290 opterr = 0;
291 while ((c = getopt(argc, argv, "3hve:o:i:l:r:g:")) != -1)
292 switch (c) {
293 case 'h':
294 if (verbose || in || out || images_done || ext) {
295 fprintf(stderr,
296 "-[?h] is exclusive with other arguments\n");
297 usage();
298 return 1;
300 usage();
301 return 0;
302 case 'v':
303 if (verbose++)
304 fprintf(stderr, "Warning: multiple -v options specified\n");
305 break;
306 case '3':
307 gen_set = 1;
308 fw_version = 3;
309 image.addr = 0x10000000;
310 break;
311 case 'g':
312 gen_set = 1;
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)) {
320 fw_version = 3;
321 image.addr = 0x10000000;
322 if ((strcasecmp(optarg, "5g") == 0) || (strcasecmp(optarg, "video") == 0)) {
323 needs_rcsc = 1;
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);
335 return 1;
337 break;
338 case 'o':
339 if (out) {
340 fprintf(stderr, "output already opened\n");
341 usage();
342 return 1;
344 if ((out = fopen(optarg, "wb+")) == NULL) {
345 fprintf(stderr, "Cannot open output file %s\n", optarg);
346 return 1;
348 break;
349 case 'e':
350 if (!out || images_done || ext) {
351 usage();
352 return 1;
355 ext = atoi(optarg) + 1;
356 break;
357 case 'i':
358 if (!out || ext) {
359 usage();
360 return 1;
362 if (images_done == 5) {
363 fprintf(stderr, "Only 5 images supported\n");
364 return 1;
366 if ((in = fopen(optarg, "rb")) == NULL) {
367 fprintf(stderr, "Cannot open firmware image file %s\n", optarg);
368 return 1;
370 if (load_entry(images + images_done, in, 0, 0) == -1)
371 return 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));
377 return 1;
379 if ((images[images_done].chksum = copysum(in, out,
380 images[images_done].len, 0x200)) == -1)
381 return 1;
382 offset += images[images_done].len;
383 if (verbose) print_image(images + images_done, "Apple image added: ");
384 images_done++;
385 fclose(in);
386 break;
387 case 'l':
388 if (!out || ext) {
389 usage();
390 return 1;
392 if (images_done == 5) {
393 fprintf(stderr, "Only 5 images supported\n");
394 return 1;
396 if ((in = fopen(optarg, "rb")) == NULL) {
397 fprintf(stderr, "Cannot open linux image file %s\n", optarg);
398 return 1;
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)
405 return 1;
406 if (fseek(out, offset, SEEK_SET) == -1) {
407 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
408 return 1;
410 if ((images[images_done].chksum = copysum(in, out,
411 images[images_done].len, 0)) == -1)
412 return 1;
413 offset += images[images_done].len;
414 if (verbose) print_image(images + images_done, "Linux image added: ");
415 images_done++;
416 fclose(in);
417 break;
418 case 'r':
419 if (ext) {
420 usage();
421 return 1;
423 version = strtol(optarg, NULL, 16);
424 break;
425 case '?':
426 fprintf(stderr, "invalid option -%c specified\n", optopt);
427 usage();
428 return 1;
429 case ':':
430 fprintf(stderr, "option -%c needs an argument\n", optopt);
431 usage();
432 return 1;
435 if (argc - optind != 1) {
436 usage();
437 return 1;
440 if (ext) {
441 if ((in = fopen(argv[optind], "rb")) == NULL) {
442 fprintf(stderr, "Cannot open firmware image file %s\n", argv[optind]);
443 return 1;
445 if (extract(in, ext-1, out) == -1) return 1;
446 fclose(in); fclose(out);
447 return 0;
449 else if (!gen_set) {
450 usage();
451 return 1;
454 printf("Generating firmware image compatible with ");
455 if (fw_version == 3) {
456 if (needs_rcsc) {
457 printf("iPod video\n");
458 } else {
459 printf("iPod mini, 4g and iPod photo/color...\n");
461 } else {
462 printf("1g, 2g and 3g iPods...\n");
465 if (!images_done) {
466 fprintf(stderr, "no images specified!\n");
467 return 1;
469 if ((in = fopen(argv[optind], "rb")) == NULL) {
470 fprintf(stderr, "Cannot open loader image file %s\n", argv[optind]);
471 return 1;
473 offset = (offset + 0x1ff) & ~0x1ff;
474 if ((len = lengthof(in)) == -1)
475 return 1;
476 if (fseek(out, offset, SEEK_SET) == -1) {
477 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
478 return 1;
480 if (copysum(in, out, len, 0) == -1)
481 return 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)
485 return 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)
491 return 1;
492 if (fseek(out, 0x0, SEEK_SET) == -1) {
493 fprintf(stderr, "fseek failed: %s\n", strerror(errno));
494 return 1;
496 if (fwrite(apple_copyright, 0x100, 1, out) != 1) {
497 fprintf(stderr, "fwrite failed: %s\n", strerror(errno));
498 return 1;
500 version = switch_32(0x5b68695d); /* magic */
501 if (fwrite(&version, 4, 1, out) != 1) {
502 fprintf(stderr, "fwrite failed: %s\n", strerror(errno));
503 return 1;
505 version = switch_32(0x00004000); /* magic */
506 if (fwrite(&version, 4, 1, out) != 1) {
507 fprintf(stderr, "fwrite failed: %s\n", strerror(errno));
508 return 1;
510 if (fw_version == 3) {
511 version = switch_32(0x0003010c); /* magic */
512 } else {
513 version = switch_32(0x0002010c); /* magic */
516 if (fwrite(&version, 4, 1, out) != 1) {
517 fprintf(stderr, "fwrite failed: %s\n", strerror(errno));
518 return 1;
520 if (write_entry(&image, out, TBL, 0) == -1)
521 return 1;
522 if (verbose) print_image(&image, "Master image: ");
524 if (needs_rcsc) {
525 image_t rsrc;
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");
529 return 1;
531 if (load_entry(&rsrc, in, 0, 0) == -1) {
532 return 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));
538 return 1;
540 if ((rsrc.chksum = copysum(in, out, rsrc.len, 0x200)) == -1) {
541 return 1;
543 fclose(in);
545 if (write_entry(&rsrc, out, TBL, 1) == -1) {
546 return 1;
549 if (verbose) print_image(&rsrc, "rsrc image: ");
552 return 0;