diff options
author | Dave Chapman <dave@dchapman.com> | 2005-11-11 17:58:05 +0000 |
---|---|---|
committer | Dave Chapman <dave@dchapman.com> | 2005-11-11 17:58:05 +0000 |
commit | 5592e37c632efb5a91685aadc9896a800f5f77af (patch) | |
tree | 8f157f98fb8abbed955fe3fac9fca0d85ef5f9cd | |
parent | d31a32c5012e4bc0eed9921e1783ed8f59d72e96 (diff) | |
download | rockbox-5592e37c632efb5a91685aadc9896a800f5f77af.tar.gz rockbox-5592e37c632efb5a91685aadc9896a800f5f77af.zip |
Import make_fw.c iPod firmware manipulation utility from the ipodlinux project. Renamed it to ipod_fw.c for use in Rockbox. This is an exact copy of the current CVS version from the ipodlinux project.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7813 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | tools/Makefile | 5 | ||||
-rw-r--r-- | tools/ipod_fw.c | 517 |
2 files changed, 521 insertions, 1 deletions
diff --git a/tools/Makefile b/tools/Makefile index faea49cb2a..1a01f0ac12 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -9,7 +9,7 @@ CFLAGS := -O -ansi -g LDFLAGS := -g -TARGETS := scramble descramble iaudio sh2d bmp2rb rdf2binary convbdf generate_rocklatin mkboot +TARGETS := scramble descramble iaudio sh2d bmp2rb rdf2binary convbdf generate_rocklatin mkboot ipod_fw all: $(TARGETS) @echo "tools done" @@ -34,6 +34,9 @@ rdf2binary: rdf2binary.c mkboot: mkboot.c $(CC) -g $+ -o $@ +ipod_fw: ipod_fw.c + $(CC) -g $+ -o $@ + convbdf: convbdf.c $(CC) -g $+ -o $@ diff --git a/tools/ipod_fw.c b/tools/ipod_fw.c new file mode 100644 index 0000000000..8f07854bc1 --- /dev/null +++ b/tools/ipod_fw.c @@ -0,0 +1,517 @@ +/* + * make_fw.c - iPodLinux loader installer + * + * 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#ifdef __WIN32__ +#include "getopt.c" +#endif + +#define TBL 0x4200 + +/* Some firmwares have padding becore the actual image. */ +#define IMAGE_PADDING ((fw_version == 3) ? 0x200 : 0) +#define FIRST_OFFSET (TBL + 0x200 + IMAGE_PADDING) + +int be; +unsigned short fw_version = 2; + +typedef struct _image { + char type[4]; /* '' */ + unsigned id; /* */ + char pad1[4]; /* 0000 0000 */ + unsigned devOffset; /* byte offset of start of image code */ + unsigned len; /* length in bytes of image */ + unsigned addr; /* load address */ + unsigned entryOffset; /* execution start within image */ + unsigned chksum; /* checksum for image */ + unsigned vers; /* image version */ + unsigned loadAddr; /* load address for image */ +} image_t; + +static char *apple_copyright = "{{~~ /-----\\ {{~~ / \\ {{~~| | {{~~| S T O P | {{~~| | {{~~ \\ / {{~~ \\-----/ Copyright(C) 2001 Apple Computer, Inc.---------------------------------------------------------------------------------------------------------"; + +unsigned +switch_32(unsigned l) +{ + if (be) + return ((l & 0xff) << 24) + | ((l & 0xff00) << 8) + | ((l & 0xff0000) >> 8) + | ((l & 0xff000000) >> 24); + return l; +} + +unsigned short +switch_16(unsigned short s) +{ + if (be) { + return ((s & 0xff) << 8) | ((s & 0xff00) >> 8); + } else { + return s; + } +} + +void +switch_endian(image_t *image) +{ + if (be) { + image->id = switch_32(image->id); + 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); + } +} + +void +print_image(image_t *image, const char *head) +{ + printf("%stype: '%s' id: 0x%4x len: 0x%4x addr: 0x%4x vers: 0x%8x\r\n", + head, image->type, image->id, 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); +} + +void +usage() +{ + printf("Usage: patch_fw [-h]\n" + " patch_fw [-v] -o outfile -e img_no fw_file\n" + " patch_fw [-v] -g gen [-r rev] -o outfile [-i img_from_-e]* [-l raw_img]* ldr_img\n\n" + " -g: set target ipod generation, valid options are: 1g, 2g, 3g\n" + " 4g, 5g, scroll, touch, dock, mini, photo, color, nano and video\n" + " -e: extract the image at img_no in boot table to outfile\n" + " fw_file is an original firmware image\n" + " the original firmware has the sw at 0, and a flash updater at 1\n" + " -i|-l: create new image to outfile\n" + " up to 5 images, any of -i or -l allowed\n" + " -i: image extracted with -e, load and entry address preserved\n" + " -l: raw image, loaded to 0x28000000, entry at 0x00000000\n" + " -r: set master revision to rev (for example 210 for 2.10)\n" + " may be needed if newest -e img is not the same as the flash rev\n" + " ldr_img is the iPodLinux loader binary.\n" + " first image is loaded by default, 2., 3., 4. or 5. loaded if\n" + " rew, menu, play or ff is hold while booting\n\n" + " -v: verbose\n\n" + " This program is used to create a bootable ipod image.\n\n"); +} + +/* read len bytes from the beginning of s, + * calculate checksum, and + * if (d) copy to current offset in d */ +unsigned +copysum(FILE *s, FILE *d, unsigned len, unsigned off) +{ + unsigned char temp; + unsigned sum = 0; + unsigned i; + if (fseek(s, off, SEEK_SET) == -1) { + fprintf(stderr, "fseek failed: %s\n", strerror(errno)); + return -1; + } + for (i = 0; i < len; i++) { + if (fread(&temp, 1, 1, s) != 1) { + fprintf(stderr, "fread failed: %s\n", strerror(errno)); + return -1; + } + sum = sum + (temp & 0xff); + if (d) + if (fwrite(&temp, 1, 1, d) != 1) { + fprintf(stderr, "fwrite failed: %s\n", strerror(errno)); + return -1; + } + } + return sum; +} + +/* load the boot entry from + * boot table at offset, + * entry number entry + * file fw */ +int +load_entry(image_t *image, FILE *fw, unsigned offset, int entry) +{ + if (fseek(fw, offset + entry * sizeof(image_t), SEEK_SET) == -1) { + fprintf(stderr, "fseek failed: %s\n", strerror(errno)); + return -1; + } + if (fread(image, sizeof(image_t), 1, fw) != 1) { + 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 */ +int +write_entry(image_t *image, FILE *fw, unsigned offset, int entry) +{ + if (fseek(fw, offset + entry * sizeof(image_t), SEEK_SET) == -1) { + fprintf(stderr, "fseek failed: %s\n", strerror(errno)); + return -1; + } + switch_endian(image); + if (fwrite(image, sizeof(image_t), 1, fw) != 1) { + fprintf(stderr, "fwrite failed: %s\n", strerror(errno)); + switch_endian(image); + return -1; + } + switch_endian(image); + return 0; +} + +/* extract a single image from the fw + * the first 40 bytes contain a boot table entry, + * padded to one block (512 bytes */ +int +extract(FILE *f, int idx, FILE *out) +{ + image_t *image; + unsigned char buf[512]; + unsigned off; + + fseek(f, 0x100 + 10, SEEK_SET); + fread(&fw_version, sizeof(fw_version), 1, f); + fw_version = switch_16(fw_version); + + image = (image_t *)buf; + if (load_entry(image, f, TBL, idx) == -1) + return -1; + off = image->devOffset + IMAGE_PADDING; + + if (fseek(f, off, SEEK_SET) == -1) { + fprintf(stderr, "fseek failed: %s\n", strerror(errno)); + return -1; + } + if (write_entry(image, out, 0x0, 0) == -1) + return -1; + if (fseek(out, 512, SEEK_SET) == -1) { + fprintf(stderr, "fseek failed: %s\n", strerror(errno)); + return -1; + } + if (copysum(f, out, image->len, off) == -1) + return -1; + + return 0; +} + +/* return the size of f */ +unsigned +lengthof(FILE *f) +{ + unsigned ret; + + if (fseek(f, 0, SEEK_END) == -1) { + fprintf(stderr, "fseek failed: %s\n", strerror(errno)); + return -1; + } + if ((ret = ftell(f)) == -1) { + fprintf(stderr, "ftell failed: %s\n", strerror(errno)); + return -1; + } + return ret; +} + +void +test_endian(void) +{ + char ch[4] = { '\0', '\1', '\2', '\3' }; + unsigned i = 0x00010203; + + if (*((int *)ch) == i) + be = 1; + else + be = 0; + return; +} + +int +main(int argc, char **argv) +{ + int c; + int verbose = 0, i, ext = 0; + FILE *in = NULL, *out = NULL; + char gen_set = 0; + image_t images[5]; + image_t image = { + { '!', 'A', 'T', 'A' }, // magic + 0x6f736f73, // id + { '\0', '\0', '\0', '\0' }, // pad + 0x4400, // devOffset + 0, // len + 0x28000000, // addr + 0, // entry + 0, // chksum + 0, // vers + 0xffffffff // loadAddr + }; + int images_done = 0; + unsigned version = 0, offset = 0, len = 0; + + test_endian(); + + /* parse options */ + opterr = 0; + while ((c = getopt(argc, argv, "3hve:o:i:l:r:g:")) != -1) + switch (c) { + case 'h': + if (verbose || in || out || images_done || ext) { + fprintf(stderr, + "-[?h] is exclusive with other arguments\n"); + usage(); + return 1; + } + usage(); + return 0; + case 'v': + if (verbose++) + fprintf(stderr, "Warning: multiple -v options specified\n"); + break; + case '3': + gen_set = 1; + fw_version = 3; + image.addr = 0x10000000; + break; + case 'g': + gen_set = 1; + if ((strcasecmp(optarg, "4g") == 0) || + (strcasecmp(optarg, "mini") == 0) || + (strcasecmp(optarg, "nano") == 0) || + (strcasecmp(optarg, "photo") == 0) || + (strcasecmp(optarg, "color") == 0) || + (strcasecmp(optarg, "video") == 0) || + (strcasecmp(optarg, "5g") == 0)) { + fw_version = 3; + image.addr = 0x10000000; + } + else if ((strcasecmp(optarg, "1g") != 0) && + (strcasecmp(optarg, "2g") != 0) && + (strcasecmp(optarg, "3g") != 0) && + (strcasecmp(optarg, "scroll") != 0) && + (strcasecmp(optarg, "touch") != 0) && + (strcasecmp(optarg, "dock") != 0)) { + fprintf(stderr, "%s: bad gen. Valid options are: 1g, 2g," + " 3g, 4g, 5g, scroll, touch, dock, mini, nano," + " photo, color, and video\n", optarg); + return 1; + } + break; + case 'o': + if (out) { + fprintf(stderr, "output already opened\n"); + usage(); + return 1; + } + if ((out = fopen(optarg, "wb+")) == NULL) { + fprintf(stderr, "Cannot open output file %s\n", optarg); + return 1; + } + break; + case 'e': + if (!out || images_done || ext) { + usage(); + return 1; + } + + ext = atoi(optarg) + 1; + break; + case 'i': + if (!out || ext) { + usage(); + return 1; + } + if (images_done == 5) { + fprintf(stderr, "Only 5 images supported\n"); + return 1; + } + if ((in = fopen(optarg, "rb")) == NULL) { + fprintf(stderr, "Cannot open firmware image file %s\n", optarg); + return 1; + } + if (load_entry(images + images_done, in, 0, 0) == -1) + return 1; + if (!offset) offset = FIRST_OFFSET; + else offset = (offset + 0x1ff) & ~0x1ff; + images[images_done].devOffset = offset; + if (fseek(out, offset, SEEK_SET) == -1) { + fprintf(stderr, "fseek failed: %s\n", strerror(errno)); + return 1; + } + if ((images[images_done].chksum = copysum(in, out, + images[images_done].len, 0x200)) == -1) + return 1; + offset += images[images_done].len; + if (verbose) print_image(images + images_done, "Apple image added: "); + images_done++; + fclose(in); + break; + case 'l': + if (!out || ext) { + usage(); + return 1; + } + if (images_done == 5) { + fprintf(stderr, "Only 5 images supported\n"); + return 1; + } + if ((in = fopen(optarg, "rb")) == NULL) { + fprintf(stderr, "Cannot open linux image file %s\n", optarg); + return 1; + } + if (!offset) offset = FIRST_OFFSET; + else offset = (offset + 0x1ff) & ~0x1ff; + images[images_done] = image; + images[images_done].devOffset = offset; + if ((images[images_done].len = lengthof(in)) == -1) + return 1; + if (fseek(out, offset, SEEK_SET) == -1) { + fprintf(stderr, "fseek failed: %s\n", strerror(errno)); + return 1; + } + if ((images[images_done].chksum = copysum(in, out, + images[images_done].len, 0)) == -1) + return 1; + offset += images[images_done].len; + if (verbose) print_image(images + images_done, "Linux image added: "); + images_done++; + fclose(in); + break; + case 'r': + if (ext) { + usage(); + return 1; + } + version = strtol(optarg, NULL, 16); + break; + case '?': + fprintf(stderr, "invalid option -%c specified\n", optopt); + usage(); + return 1; + case ':': + fprintf(stderr, "option -%c needs an argument\n", optopt); + usage(); + return 1; + } + + if (argc - optind != 1) { + usage(); + return 1; + } + + if (ext) { + if ((in = fopen(argv[optind], "rb")) == NULL) { + fprintf(stderr, "Cannot open firmware image file %s\n", argv[optind]); + return 1; + } + if (extract(in, ext-1, out) == -1) return 1; + fclose(in); fclose(out); + return 0; + } + else if (!gen_set) { + usage(); + return 1; + } + + printf("Generating firmware image compatible with "); + if (fw_version == 3) { + printf("iPod mini, 4g and iPod photo...\n"); + } else { + printf("1g, 2g and 3g iPods...\n"); + } + + if (!images_done) { + fprintf(stderr, "no images specified!\n"); + return 1; + } + if ((in = fopen(argv[optind], "rb")) == NULL) { + fprintf(stderr, "Cannot open loader image file %s\n", argv[optind]); + return 1; + } + offset = (offset + 0x1ff) & ~0x1ff; + if ((len = lengthof(in)) == -1) + return 1; + if (fseek(out, offset, SEEK_SET) == -1) { + fprintf(stderr, "fseek failed: %s\n", strerror(errno)); + return 1; + } + if (copysum(in, out, len, 0) == -1) + return 1; + for (i=0; i < images_done; i++) { + if (images[i].vers > image.vers) image.vers = images[i].vers; + if (write_entry(images+i, out, offset+0x0100, i) == -1) + return 1; + } + if (version) image.vers = version; + image.len = offset + len - FIRST_OFFSET; + image.entryOffset = offset - FIRST_OFFSET; + if ((image.chksum = copysum(out, NULL, image.len, FIRST_OFFSET)) == -1) + return 1; + if (fseek(out, 0x0, SEEK_SET) == -1) { + fprintf(stderr, "fseek failed: %s\n", strerror(errno)); + return 1; + } + if (fwrite(apple_copyright, 0x100, 1, out) != 1) { + fprintf(stderr, "fwrite failed: %s\n", strerror(errno)); + return 1; + } + version = switch_32(0x5b68695d); /* magic */ + if (fwrite(&version, 4, 1, out) != 1) { + fprintf(stderr, "fwrite failed: %s\n", strerror(errno)); + return 1; + } + version = switch_32(0x00004000); /* magic */ + if (fwrite(&version, 4, 1, out) != 1) { + fprintf(stderr, "fwrite failed: %s\n", strerror(errno)); + return 1; + } + if (fw_version == 3) { + version = switch_32(0x0003010c); /* magic */ + } else { + version = switch_32(0x0002010c); /* magic */ + } + + if (fwrite(&version, 4, 1, out) != 1) { + fprintf(stderr, "fwrite failed: %s\n", strerror(errno)); + return 1; + } + if (write_entry(&image, out, TBL, 0) == -1) + return 1; + if (verbose) print_image(&image, "Master image: "); + return 0; +} + |