blob: 08ba9263d2bf133c9f2070cafb0e9b840644b3fc [file] [log] [blame]
Dave Chapman45895df2006-12-13 08:57:06 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
Dave Chapmane332f4c2007-02-05 01:20:20 +000010 * Copyright (C) 2006-2007 Dave Chapman
Dave Chapman45895df2006-12-13 08:57:06 +000011 *
Dave Chapman45895df2006-12-13 08:57:06 +000012 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include <stdio.h>
21#include <unistd.h>
22#include <fcntl.h>
23#include <string.h>
24#include <stdlib.h>
Dave Chapman4b7e1e02006-12-13 09:02:18 +000025#include <inttypes.h>
Dave Chapman6e797152007-09-08 23:27:49 +000026#include <stdbool.h>
Dave Chapman45895df2006-12-13 08:57:06 +000027#include <sys/types.h>
28#include <sys/stat.h>
Dave Chapman45895df2006-12-13 08:57:06 +000029
30#include "parttypes.h"
Dave Chapman4b7e1e02006-12-13 09:02:18 +000031#include "ipodio.h"
Dave Chapman79a1fb12007-02-08 23:57:41 +000032#include "ipodpatcher.h"
Dave Chapman4b7e1e02006-12-13 09:02:18 +000033
Dave Chapmanbdc27ff2007-02-08 18:05:50 +000034#ifdef WITH_BOOTOBJS
Dave Chapman6a20def2007-07-26 20:21:11 +000035#include "ipod1g2g.h"
Dave Chapmanbdc27ff2007-02-08 18:05:50 +000036#include "ipod3g.h"
37#include "ipod4g.h"
38#include "ipodmini.h"
39#include "ipodmini2g.h"
40#include "ipodcolor.h"
41#include "ipodnano.h"
42#include "ipodvideo.h"
43#endif
44
Dave Chapman6e797152007-09-08 23:27:49 +000045#ifndef RBUTIL
46#include "arc4.h"
47#endif
48
Dave Chapman79a1fb12007-02-08 23:57:41 +000049extern int verbose;
Dave Chapman4b7e1e02006-12-13 09:02:18 +000050
Dave Chapman79a1fb12007-02-08 23:57:41 +000051unsigned char* sectorbuf;
Dave Chapman4b7e1e02006-12-13 09:02:18 +000052
53/* The following string appears at the start of the firmware partition */
54static const char *apple_stop_sign = "{{~~ /-----\\ "\
55 "{{~~ / \\ "\
56 "{{~~| | "\
57 "{{~~| S T O P | "\
58 "{{~~| | "\
59 "{{~~ \\ / "\
60 "{{~~ \\-----/ "\
61 "Copyright(C) 200"\
62 "1 Apple Computer"\
63 ", Inc.----------"\
64 "----------------"\
65 "----------------"\
66 "----------------"\
67 "----------------"\
68 "----------------"\
69 "---------------";
70
Dave Chapman45895df2006-12-13 08:57:06 +000071/* Windows requires the buffer for disk I/O to be aligned in memory on a
72 multiple of the disk volume size - so we use a single global variable
Dave Chapman4b7e1e02006-12-13 09:02:18 +000073 and initialise it with ipod_alloc_buf()
Dave Chapman45895df2006-12-13 08:57:06 +000074*/
Dave Chapman4b7e1e02006-12-13 09:02:18 +000075
Dave Chapman45895df2006-12-13 08:57:06 +000076char* get_parttype(int pt)
77{
78 int i;
79 static char unknown[]="Unknown";
80
Dave Chapman2cc80f52007-07-29 21:19:14 +000081 if (pt == PARTTYPE_HFS) {
Dave Chapman6e641e82007-02-05 18:29:39 +000082 return "HFS/HFS+";
83 }
84
Dave Chapman45895df2006-12-13 08:57:06 +000085 i=0;
86 while (parttypes[i].name != NULL) {
87 if (parttypes[i].type == pt) {
88 return (parttypes[i].name);
89 }
90 i++;
91 }
92
93 return unknown;
94}
95
Dave Chapman45895df2006-12-13 08:57:06 +000096off_t filesize(int fd) {
97 struct stat buf;
98
99 if (fstat(fd,&buf) < 0) {
100 perror("[ERR] Checking filesize of input file");
101 return -1;
102 } else {
103 return(buf.st_size);
104 }
105}
106
Dave Chapman45895df2006-12-13 08:57:06 +0000107/* Partition table parsing code taken from Rockbox */
108
Dave Chapman206238d2006-12-13 08:59:07 +0000109#define MAX_SECTOR_SIZE 2048
Dave Chapman45895df2006-12-13 08:57:06 +0000110#define SECTOR_SIZE 512
111
Dave Chapman2cc80f52007-07-29 21:19:14 +0000112static inline unsigned short le2ushort(unsigned char* buf)
Dave Chapman31aa4522007-02-04 11:42:11 +0000113{
114 unsigned short res = (buf[1] << 8) | buf[0];
115
116 return res;
117}
Dave Chapman45895df2006-12-13 08:57:06 +0000118
Dave Chapman2cc80f52007-07-29 21:19:14 +0000119static inline int le2int(unsigned char* buf)
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000120{
121 int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
122
123 return res;
124}
125
Dave Chapman2cc80f52007-07-29 21:19:14 +0000126static inline int be2int(unsigned char* buf)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000127{
128 int32_t res = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
129
130 return res;
131}
132
Dave Chapman2cc80f52007-07-29 21:19:14 +0000133static inline int getint16le(char* buf)
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000134{
135 int16_t res = (buf[1] << 8) | buf[0];
136
137 return res;
138}
139
Dave Chapman2cc80f52007-07-29 21:19:14 +0000140static inline void short2le(unsigned short val, unsigned char* addr)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000141{
142 addr[0] = val & 0xFF;
143 addr[1] = (val >> 8) & 0xff;
144}
145
Dave Chapman2cc80f52007-07-29 21:19:14 +0000146static inline void int2le(unsigned int val, unsigned char* addr)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000147{
148 addr[0] = val & 0xFF;
149 addr[1] = (val >> 8) & 0xff;
150 addr[2] = (val >> 16) & 0xff;
151 addr[3] = (val >> 24) & 0xff;
152}
153
Dave Chapman2cc80f52007-07-29 21:19:14 +0000154static inline void int2be(unsigned int val, unsigned char* addr)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000155{
156 addr[0] = (val >> 24) & 0xff;
157 addr[1] = (val >> 16) & 0xff;
158 addr[2] = (val >> 8) & 0xff;
159 addr[3] = val & 0xFF;
160}
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000161
162
163#define BYTES2INT32(array,pos)\
164 ((long)array[pos] | ((long)array[pos+1] << 8 ) |\
Dave Chapman45895df2006-12-13 08:57:06 +0000165 ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
166
Dave Chapman31aa4522007-02-04 11:42:11 +0000167int read_partinfo(struct ipod_t* ipod, int silent)
Dave Chapman45895df2006-12-13 08:57:06 +0000168{
169 int i;
Dave Chapman45895df2006-12-13 08:57:06 +0000170 unsigned long count;
171
Dave Chapman31aa4522007-02-04 11:42:11 +0000172 count = ipod_read(ipod,sectorbuf, ipod->sector_size);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000173
174 if (count <= 0) {
175 print_error(" Error reading from disk: ");
Dave Chapman45895df2006-12-13 08:57:06 +0000176 return -1;
177 }
178
Dave Chapman35735c662007-07-27 20:51:36 +0000179 memset(ipod->pinfo, 0, sizeof(ipod->pinfo));
180
Dave Chapman6e641e82007-02-05 18:29:39 +0000181 if ((sectorbuf[510] == 0x55) && (sectorbuf[511] == 0xaa)) {
182 /* DOS partition table */
Dave Chapman6e641e82007-02-05 18:29:39 +0000183 ipod->macpod = 0;
184 /* parse partitions */
185 for ( i = 0; i < 4; i++ ) {
186 unsigned char* ptr = sectorbuf + 0x1be + 16*i;
187 ipod->pinfo[i].type = ptr[4];
188 ipod->pinfo[i].start = BYTES2INT32(ptr, 8);
189 ipod->pinfo[i].size = BYTES2INT32(ptr, 12);
190
191 /* extended? */
192 if ( ipod->pinfo[i].type == 5 ) {
193 /* not handled yet */
194 }
195 }
196 } else if ((sectorbuf[0] == 'E') && (sectorbuf[1] == 'R')) {
197 /* Apple Partition Map */
198
199 /* APM parsing code based on the check_mac_partitions() function in
200 ipodloader2 - written by Thomas Tempelmann and released
201 under the GPL. */
202
203 int blkNo = 1;
204 int partBlkCount = 1;
205 int partBlkSizMul = sectorbuf[2] / 2;
206
207 int pmMapBlkCnt; /* # of blks in partition map */
208 int pmPyPartStart; /* physical start blk of partition */
209 int pmPartBlkCnt; /* # of blks in this partition */
210 int i = 0;
211
212 ipod->macpod = 1;
213
214 memset(ipod->pinfo,0,sizeof(ipod->pinfo));
215
216 while (blkNo <= partBlkCount) {
217 if (ipod_seek(ipod, blkNo * partBlkSizMul * 512) < 0) {
218 fprintf(stderr,"[ERR] Seek failed whilst reading APM\n");
219 return -1;
220 }
221
222 count = ipod_read(ipod, sectorbuf, ipod->sector_size);
223
224 if (count <= 0) {
225 print_error(" Error reading from disk: ");
226 return -1;
227 }
228
229 /* see if it's a partition entry */
230 if ((sectorbuf[0] != 'P') || (sectorbuf[1] != 'M')) {
231 /* end of partition table -> leave the loop */
232 break;
233 }
234
235 /* Extract the interesting entries */
236 pmMapBlkCnt = be2int(sectorbuf + 4);
237 pmPyPartStart = be2int(sectorbuf + 8);
238 pmPartBlkCnt = be2int(sectorbuf + 12);
239
240 /* update the number of part map blocks */
241 partBlkCount = pmMapBlkCnt;
242
243 if (strncmp((char*)(sectorbuf + 48), "Apple_MDFW", 32)==0) {
244 /* A Firmware partition */
245 ipod->pinfo[i].start = pmPyPartStart;
246 ipod->pinfo[i].size = pmPartBlkCnt;
247 ipod->pinfo[i].type = 0;
248 i++;
249 } else if (strncmp((char*)(sectorbuf + 48), "Apple_HFS", 32)==0) {
250 /* A HFS partition */
251 ipod->pinfo[i].start = pmPyPartStart;
252 ipod->pinfo[i].size = pmPartBlkCnt;
Dave Chapman2cc80f52007-07-29 21:19:14 +0000253 ipod->pinfo[i].type = PARTTYPE_HFS;
Dave Chapman6e641e82007-02-05 18:29:39 +0000254 i++;
255 }
256
257 blkNo++; /* read next partition map entry */
258 }
259 } else {
Dave Chapman57b84b62006-12-17 23:00:15 +0000260 if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n");
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000261 return -1;
Dave Chapman45895df2006-12-13 08:57:06 +0000262 }
263
Dave Chapman4df0c772007-05-22 19:07:45 +0000264 /* Check that the partition table looks like an ipod:
265 1) Partition 1 is of type 0 (Empty) but isn't empty.
Dave Chapman0fba85f2007-06-10 22:47:51 +0000266 2) Partition 2 is of type 0xb or 0xc (winpod) or -1 (macpod)
Dave Chapman4df0c772007-05-22 19:07:45 +0000267 */
268 if ((ipod->pinfo[0].type != 0) || (ipod->pinfo[0].size == 0) ||
Dave Chapman0fba85f2007-06-10 22:47:51 +0000269 ((ipod->pinfo[1].type != 0xb) && (ipod->pinfo[1].type != 0xc) &&
Dave Chapman2cc80f52007-07-29 21:19:14 +0000270 (ipod->pinfo[1].type != PARTTYPE_HFS))) {
Dave Chapman4df0c772007-05-22 19:07:45 +0000271 if (!silent) fprintf(stderr,"[ERR] Partition layout is not an ipod\n");
272 return -1;
273 }
274
Dave Chapman31aa4522007-02-04 11:42:11 +0000275 ipod->start = ipod->pinfo[0].start*ipod->sector_size;
Dave Chapman45895df2006-12-13 08:57:06 +0000276 return 0;
277}
278
Dave Chapman31aa4522007-02-04 11:42:11 +0000279int read_partition(struct ipod_t* ipod, int outfile)
Dave Chapman45895df2006-12-13 08:57:06 +0000280{
281 int res;
Dave Chapman2cc80f52007-07-29 21:19:14 +0000282 ssize_t n;
Dave Chapman45895df2006-12-13 08:57:06 +0000283 int bytesleft;
284 int chunksize;
Dave Chapman31aa4522007-02-04 11:42:11 +0000285 int count = ipod->pinfo[0].size;
Dave Chapman45895df2006-12-13 08:57:06 +0000286
Dave Chapman31aa4522007-02-04 11:42:11 +0000287 if (ipod_seek(ipod, ipod->start) < 0) {
Dave Chapman45895df2006-12-13 08:57:06 +0000288 return -1;
289 }
290
Dave Chapman31aa4522007-02-04 11:42:11 +0000291 fprintf(stderr,"[INFO] Writing %d sectors to output file\n",count);
Dave Chapman45895df2006-12-13 08:57:06 +0000292
Dave Chapman31aa4522007-02-04 11:42:11 +0000293 bytesleft = count * ipod->sector_size;
Dave Chapman45895df2006-12-13 08:57:06 +0000294 while (bytesleft > 0) {
295 if (bytesleft > BUFFER_SIZE) {
296 chunksize = BUFFER_SIZE;
297 } else {
298 chunksize = bytesleft;
299 }
300
Dave Chapman31aa4522007-02-04 11:42:11 +0000301 n = ipod_read(ipod, sectorbuf, chunksize);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000302
303 if (n < 0) {
Dave Chapman45895df2006-12-13 08:57:06 +0000304 return -1;
305 }
306
307 if (n < chunksize) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000308 fprintf(stderr,
Dave Chapman2cc80f52007-07-29 21:19:14 +0000309 "[ERR] Short read in disk_read() - requested %d, got %d\n",
Dave Chapman0be30362007-07-29 21:25:09 +0000310 chunksize,(int)n);
Dave Chapman45895df2006-12-13 08:57:06 +0000311 return -1;
312 }
313
314 bytesleft -= n;
315
316 res = write(outfile,sectorbuf,n);
317
318 if (res < 0) {
319 perror("[ERR] write in disk_read");
320 return -1;
321 }
322
323 if (res != n) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000324 fprintf(stderr,
Dave Chapman0be30362007-07-29 21:25:09 +0000325 "Short write - requested %d, received %d - aborting.\n",(int)n,res);
Dave Chapman45895df2006-12-13 08:57:06 +0000326 return -1;
327 }
328 }
329
330 fprintf(stderr,"[INFO] Done.\n");
331 return 0;
332}
333
Dave Chapman31aa4522007-02-04 11:42:11 +0000334int write_partition(struct ipod_t* ipod, int infile)
Dave Chapman45895df2006-12-13 08:57:06 +0000335{
Dave Chapman2cc80f52007-07-29 21:19:14 +0000336 ssize_t res;
Dave Chapman45895df2006-12-13 08:57:06 +0000337 int n;
338 int bytesread;
339 int byteswritten = 0;
340 int eof;
341 int padding = 0;
342
Dave Chapman31aa4522007-02-04 11:42:11 +0000343 if (ipod_seek(ipod, ipod->start) < 0) {
Dave Chapman45895df2006-12-13 08:57:06 +0000344 return -1;
345 }
346
347 fprintf(stderr,"[INFO] Writing input file to device\n");
348 bytesread = 0;
349 eof = 0;
350 while (!eof) {
351 n = read(infile,sectorbuf,BUFFER_SIZE);
352
353 if (n < 0) {
354 perror("[ERR] read in disk_write");
355 return -1;
356 }
357
358 if (n < BUFFER_SIZE) {
359 eof = 1;
360 /* We need to pad the last write to a multiple of SECTOR_SIZE */
Dave Chapman31aa4522007-02-04 11:42:11 +0000361 if ((n % ipod->sector_size) != 0) {
362 padding = (ipod->sector_size-(n % ipod->sector_size));
Dave Chapman45895df2006-12-13 08:57:06 +0000363 n += padding;
364 }
365 }
366
367 bytesread += n;
368
Dave Chapman31aa4522007-02-04 11:42:11 +0000369 res = ipod_write(ipod, sectorbuf, n);
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000370
371 if (res < 0) {
372 print_error(" Error writing to disk: ");
Dave Chapman45895df2006-12-13 08:57:06 +0000373 fprintf(stderr,"Bytes written: %d\n",byteswritten);
374 return -1;
375 }
376
377 if (res != n) {
Dave Chapman0be30362007-07-29 21:25:09 +0000378 fprintf(stderr,"[ERR] Short write - requested %d, received %d - aborting.\n",n,(int)res);
Dave Chapman45895df2006-12-13 08:57:06 +0000379 return -1;
380 }
381
382 byteswritten += res;
383 }
384
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000385 fprintf(stderr,"[INFO] Wrote %d bytes plus %d bytes padding.\n",
386 byteswritten-padding,padding);
Dave Chapman45895df2006-12-13 08:57:06 +0000387 return 0;
388}
389
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000390char* ftypename[] = { "OSOS", "RSRC", "AUPD", "HIBE" };
391
Dave Chapman31aa4522007-02-04 11:42:11 +0000392int diskmove(struct ipod_t* ipod, int delta)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000393{
Dave Chapman75a11122006-12-14 18:41:03 +0000394 int src_start;
395 int src_end;
Dave Chapman75a11122006-12-14 18:41:03 +0000396 int bytesleft;
397 int chunksize;
Dave Chapman75a11122006-12-14 18:41:03 +0000398 int n;
399
Dave Chapman6e797152007-09-08 23:27:49 +0000400 src_start = ipod->ipod_directory[1].devOffset;
Dave Chapman31aa4522007-02-04 11:42:11 +0000401 src_end = (ipod->ipod_directory[ipod->nimages-1].devOffset + ipod->sector_size +
402 ipod->ipod_directory[ipod->nimages-1].len +
403 (ipod->sector_size-1)) & ~(ipod->sector_size-1);
Dave Chapman75a11122006-12-14 18:41:03 +0000404 bytesleft = src_end - src_start;
Dave Chapman75a11122006-12-14 18:41:03 +0000405
406 if (verbose) {
Dave Chapman31aa4522007-02-04 11:42:11 +0000407 fprintf(stderr,"[INFO] Need to move images 2-%d forward %08x bytes\n", ipod->nimages,delta);
Dave Chapman75a11122006-12-14 18:41:03 +0000408 fprintf(stderr,"[VERB] src_start = %08x\n",src_start);
409 fprintf(stderr,"[VERB] src_end = %08x\n",src_end);
Dave Chapman408c65b2006-12-15 09:55:21 +0000410 fprintf(stderr,"[VERB] dest_start = %08x\n",src_start+delta);
411 fprintf(stderr,"[VERB] dest_end = %08x\n",src_end+delta);
Dave Chapman75a11122006-12-14 18:41:03 +0000412 fprintf(stderr,"[VERB] bytes to copy = %08x\n",bytesleft);
413 }
414
415 while (bytesleft > 0) {
416 if (bytesleft <= BUFFER_SIZE) {
417 chunksize = bytesleft;
418 } else {
419 chunksize = BUFFER_SIZE;
420 }
421
422 if (verbose) {
Dave Chapman408c65b2006-12-15 09:55:21 +0000423 fprintf(stderr,"[VERB] Copying %08x bytes from %08x to %08x (absolute %08x to %08x)\n",
Dave Chapman75a11122006-12-14 18:41:03 +0000424 chunksize,
Dave Chapman408c65b2006-12-15 09:55:21 +0000425 src_end-chunksize,
426 src_end-chunksize+delta,
Dave Chapman31aa4522007-02-04 11:42:11 +0000427 (unsigned int)(ipod->start+src_end-chunksize),
428 (unsigned int)(ipod->start+src_end-chunksize+delta));
Dave Chapman75a11122006-12-14 18:41:03 +0000429 }
430
431
Dave Chapman31aa4522007-02-04 11:42:11 +0000432 if (ipod_seek(ipod, ipod->start+src_end-chunksize) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000433 fprintf(stderr,"[ERR] Seek failed\n");
434 return -1;
435 }
436
Dave Chapman31aa4522007-02-04 11:42:11 +0000437 if ((n = ipod_read(ipod,sectorbuf,chunksize)) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000438 perror("[ERR] Write failed\n");
439 return -1;
440 }
441
442 if (n < chunksize) {
443 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
Dave Chapman3b1119b2007-07-29 20:43:42 +0000444 chunksize,n);
Dave Chapman75a11122006-12-14 18:41:03 +0000445 return -1;
446 }
447
Dave Chapman31aa4522007-02-04 11:42:11 +0000448 if (ipod_seek(ipod, ipod->start+src_end-chunksize+delta) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000449 fprintf(stderr,"[ERR] Seek failed\n");
450 return -1;
451 }
452
Dave Chapman31aa4522007-02-04 11:42:11 +0000453 if ((n = ipod_write(ipod,sectorbuf,chunksize)) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000454 perror("[ERR] Write failed\n");
455 return -1;
456 }
457
458 if (n < chunksize) {
459 fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n"
Dave Chapman3b1119b2007-07-29 20:43:42 +0000460 ,chunksize,n);
Dave Chapman75a11122006-12-14 18:41:03 +0000461 return -1;
462 }
463
Dave Chapman408c65b2006-12-15 09:55:21 +0000464 src_end -= chunksize;
Dave Chapman75a11122006-12-14 18:41:03 +0000465 bytesleft -= chunksize;
466 }
467
468 return 0;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000469}
470
Dave Chapman31aa4522007-02-04 11:42:11 +0000471int add_bootloader(struct ipod_t* ipod, char* filename, int type)
Dave Chapman75a11122006-12-14 18:41:03 +0000472{
473 int length;
474 int i;
Dave Chapmand7729772006-12-15 15:52:08 +0000475 int x;
Dave Chapman75a11122006-12-14 18:41:03 +0000476 int n;
477 int infile;
478 int paddedlength;
479 int entryOffset;
480 int delta = 0;
481 unsigned long chksum=0;
482 unsigned long filechksum=0;
483 unsigned char header[8]; /* Header for .ipod file */
Dave Chapmaneca52222007-02-08 21:31:38 +0000484 unsigned char* bootloader_buf;
Dave Chapman75a11122006-12-14 18:41:03 +0000485
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000486 /* Calculate the position in the OSOS image where our bootloader will go. */
Dave Chapman31aa4522007-02-04 11:42:11 +0000487 if (ipod->ipod_directory[0].entryOffset>0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000488 /* Keep the same entryOffset */
Dave Chapman31aa4522007-02-04 11:42:11 +0000489 entryOffset = ipod->ipod_directory[0].entryOffset;
Dave Chapman75a11122006-12-14 18:41:03 +0000490 } else {
Dave Chapman31aa4522007-02-04 11:42:11 +0000491 entryOffset = (ipod->ipod_directory[0].len+ipod->sector_size-1)&~(ipod->sector_size-1);
Dave Chapman75a11122006-12-14 18:41:03 +0000492 }
493
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000494#ifdef WITH_BOOTOBJS
495 if (type == FILETYPE_INTERNAL) {
496 fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len);
497 memcpy(sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len);
498 length = ipod->bootloader_len;
499 paddedlength=(ipod->bootloader_len+ipod->sector_size-1)&~(ipod->sector_size-1);
500 }
501 else
502#endif
503 {
504 infile=open(filename,O_RDONLY);
505 if (infile < 0) {
506 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
507 return -1;
508 }
509
510 if (type==FILETYPE_DOT_IPOD) {
511 /* First check that the input file is the correct type for this ipod. */
512 n = read(infile,header,8);
513 if (n < 8) {
514 fprintf(stderr,"[ERR] Failed to read header from %s\n",filename);
515 close(infile);
516 return -1;
517 }
518
519 if (memcmp(header+4, ipod->modelname,4)!=0) {
520 fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n",
521 header[4],header[5],header[6],header[7], ipod->modelname);
522 close(infile);
523 return -1;
524 }
525
526 filechksum = be2int(header);
527
528 length=filesize(infile)-8;
529 } else {
530 length=filesize(infile);
531 }
532 paddedlength=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
Dave Chapmaneca52222007-02-08 21:31:38 +0000533
534 bootloader_buf = malloc(length);
535 if (bootloader_buf == NULL) {
Dave Chapman6e314e52007-06-08 15:24:47 +0000536 fprintf(stderr,"[ERR] Can not allocate memory for bootloader\n");
Dave Chapman09b247f2007-06-08 15:30:14 +0000537 return -1;
Dave Chapmaneca52222007-02-08 21:31:38 +0000538 }
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000539 /* Now read our bootloader - we need to check it before modifying the partition*/
Dave Chapmaneca52222007-02-08 21:31:38 +0000540 n = read(infile,bootloader_buf,length);
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000541 close(infile);
542
543 if (n < 0) {
544 fprintf(stderr,"[ERR] Couldn't read input file\n");
545 return -1;
546 }
547
548 if (type==FILETYPE_DOT_IPOD) {
549 /* Calculate and confirm bootloader checksum */
550 chksum = ipod->modelnum;
Dave Chapmaneca52222007-02-08 21:31:38 +0000551 for (i = 0; i < length; i++) {
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000552 /* add 8 unsigned bits but keep a 32 bit sum */
Dave Chapmaneca52222007-02-08 21:31:38 +0000553 chksum += bootloader_buf[i];
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000554 }
555
556 if (chksum == filechksum) {
557 fprintf(stderr,"[INFO] Checksum OK in %s\n",filename);
558 } else {
559 fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename);
560 return -1;
561 }
562 }
563 }
564
Dave Chapman75a11122006-12-14 18:41:03 +0000565 if (entryOffset+paddedlength > BUFFER_SIZE) {
566 fprintf(stderr,"[ERR] Input file too big for buffer\n");
Dave Chapman75a11122006-12-14 18:41:03 +0000567 return -1;
568 }
569
570 if (verbose) {
Dave Chapman31aa4522007-02-04 11:42:11 +0000571 fprintf(stderr,"[VERB] Original firmware begins at 0x%08x\n", ipod->ipod_directory[0].devOffset + ipod->sector_size);
Dave Chapman75a11122006-12-14 18:41:03 +0000572 fprintf(stderr,"[VERB] New entryOffset will be 0x%08x\n",entryOffset);
573 fprintf(stderr,"[VERB] End of bootloader will be at 0x%08x\n",entryOffset+paddedlength);
574 }
575
576 /* Check if we have enough space */
577 /* TODO: Check the size of the partition. */
Dave Chapman31aa4522007-02-04 11:42:11 +0000578 if (ipod->nimages > 1) {
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000579 if ((ipod->ipod_directory[0].devOffset+entryOffset+paddedlength) >
Dave Chapman33e15152007-02-04 13:04:23 +0000580 ipod->ipod_directory[1].devOffset) {
Dave Chapman75a11122006-12-14 18:41:03 +0000581 fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n");
Dave Chapman31aa4522007-02-04 11:42:11 +0000582 delta = ipod->ipod_directory[0].devOffset + entryOffset+paddedlength
Dave Chapman6e797152007-09-08 23:27:49 +0000583 - ipod->ipod_directory[1].devOffset + ipod->sector_size;
Dave Chapman75a11122006-12-14 18:41:03 +0000584
Dave Chapman31aa4522007-02-04 11:42:11 +0000585 if (diskmove(ipod, delta) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000586 fprintf(stderr,"[ERR] Image movement failed.\n");
587 return -1;
588 }
589 }
590 }
591
592
593 /* We have moved the partitions, now we can write our bootloader */
594
595 /* Firstly read the original firmware into sectorbuf */
596 fprintf(stderr,"[INFO] Reading original firmware...\n");
Dave Chapman31aa4522007-02-04 11:42:11 +0000597 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000598 fprintf(stderr,"[ERR] Seek failed\n");
599 return -1;
600 }
601
Dave Chapman31aa4522007-02-04 11:42:11 +0000602 if ((n = ipod_read(ipod,sectorbuf,entryOffset)) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000603 perror("[ERR] Read failed\n");
604 return -1;
605 }
606
607 if (n < entryOffset) {
608 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n"
Dave Chapman3b1119b2007-07-29 20:43:42 +0000609 ,entryOffset,n);
Dave Chapman75a11122006-12-14 18:41:03 +0000610 return -1;
611 }
612
Dave Chapmaneca52222007-02-08 21:31:38 +0000613#ifdef WITH_BOOTOBJS
614 if (type == FILETYPE_INTERNAL) {
615 memcpy(sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len);
616 }
617 else
618#endif
619 {
620 memcpy(sectorbuf+entryOffset,bootloader_buf,length);
621 free(bootloader_buf);
622 }
623
Dave Chapman75a11122006-12-14 18:41:03 +0000624 /* Calculate new checksum for combined image */
625 chksum = 0;
626 for (i=0;i<entryOffset + length; i++) {
627 chksum += sectorbuf[i];
628 }
629
630 /* Now write the combined firmware image to the disk */
631
Dave Chapman31aa4522007-02-04 11:42:11 +0000632 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000633 fprintf(stderr,"[ERR] Seek failed\n");
634 return -1;
635 }
636
Dave Chapman31aa4522007-02-04 11:42:11 +0000637 if ((n = ipod_write(ipod,sectorbuf,entryOffset+paddedlength)) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000638 perror("[ERR] Write failed\n");
639 return -1;
640 }
641
642 if (n < (entryOffset+paddedlength)) {
643 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n"
Dave Chapman3b1119b2007-07-29 20:43:42 +0000644 ,entryOffset+paddedlength,n);
Dave Chapman75a11122006-12-14 18:41:03 +0000645 return -1;
646 }
647
648 fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",entryOffset+paddedlength);
649
Dave Chapman31aa4522007-02-04 11:42:11 +0000650 x = ipod->diroffset % ipod->sector_size;
Dave Chapman75a11122006-12-14 18:41:03 +0000651
652 /* Read directory */
Dave Chapman31aa4522007-02-04 11:42:11 +0000653 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) {
654 fprintf(stderr,"[ERR] Seek failed\n");
655 return -1;
656 }
Dave Chapman75a11122006-12-14 18:41:03 +0000657
Dave Chapman31aa4522007-02-04 11:42:11 +0000658 n=ipod_read(ipod, sectorbuf, ipod->sector_size);
659 if (n < 0) {
660 fprintf(stderr,"[ERR] Directory read failed\n");
661 return -1;
662 }
Dave Chapman75a11122006-12-14 18:41:03 +0000663
664 /* Update entries for image 0 */
Dave Chapmand7729772006-12-15 15:52:08 +0000665 int2le(entryOffset+length,sectorbuf+x+16);
666 int2le(entryOffset,sectorbuf+x+24);
667 int2le(chksum,sectorbuf+x+28);
Dave Chapman31aa4522007-02-04 11:42:11 +0000668 int2le(0xffffffff,sectorbuf+x+36); /* loadAddr */
Dave Chapman75a11122006-12-14 18:41:03 +0000669
670 /* Update devOffset entries for other images, if we have moved them */
671 if (delta > 0) {
Dave Chapman31aa4522007-02-04 11:42:11 +0000672 for (i=1;i<ipod->nimages;i++) {
Dave Chapmand7729772006-12-15 15:52:08 +0000673 int2le(le2int(sectorbuf+x+i*40+12)+delta,sectorbuf+x+i*40+12);
Dave Chapman75a11122006-12-14 18:41:03 +0000674 }
675 }
676
677 /* Write directory */
Dave Chapman31aa4522007-02-04 11:42:11 +0000678 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) {
Dave Chapmanc500b962007-02-04 23:41:47 +0000679 fprintf(stderr,"[ERR] Seek to %d failed\n", (int)(ipod->start+ipod->diroffset-x));
Dave Chapman31aa4522007-02-04 11:42:11 +0000680 return -1;
681 }
682 n=ipod_write(ipod, sectorbuf, ipod->sector_size);
683 if (n < 0) {
684 fprintf(stderr,"[ERR] Directory write failed\n");
685 return -1;
686 }
Dave Chapman75a11122006-12-14 18:41:03 +0000687
688 return 0;
689}
690
Dave Chapman31aa4522007-02-04 11:42:11 +0000691int delete_bootloader(struct ipod_t* ipod)
Dave Chapman75a11122006-12-14 18:41:03 +0000692{
693 int length;
694 int i;
Dave Chapmand7729772006-12-15 15:52:08 +0000695 int x;
Dave Chapman75a11122006-12-14 18:41:03 +0000696 int n;
697 unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/
698
699 /* Removing the bootloader involves adjusting the "length",
700 "chksum" and "entryOffset" values in the osos image's directory
701 entry. */
702
703 /* Firstly check we have a bootloader... */
704
Dave Chapman31aa4522007-02-04 11:42:11 +0000705 if (ipod->ipod_directory[0].entryOffset == 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000706 fprintf(stderr,"[ERR] No bootloader found.\n");
707 return -1;
708 }
709
Dave Chapman31aa4522007-02-04 11:42:11 +0000710 length = ipod->ipod_directory[0].entryOffset;
Dave Chapman75a11122006-12-14 18:41:03 +0000711
712 /* Read the firmware so we can calculate the checksum */
713 fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length);
714
Dave Chapman31aa4522007-02-04 11:42:11 +0000715 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000716 return -1;
717 }
718
Dave Chapman31aa4522007-02-04 11:42:11 +0000719 i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1);
Dave Chapman75a11122006-12-14 18:41:03 +0000720 fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n",
721 length,i);
722
Dave Chapman31aa4522007-02-04 11:42:11 +0000723 if ((n = ipod_read(ipod,sectorbuf,i)) < 0) {
Dave Chapman75a11122006-12-14 18:41:03 +0000724 return -1;
725 }
726
727 if (n < i) {
728 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
729 i,n);
730 return -1;
731 }
732
733 chksum = 0;
734 for (i = 0; i < length; i++) {
735 /* add 8 unsigned bits but keep a 32 bit sum */
736 chksum += sectorbuf[i];
737 }
738
739 /* Now write back the updated directory entry */
740
741 fprintf(stderr,"[INFO] Updating firmware checksum\n");
742
Dave Chapman31aa4522007-02-04 11:42:11 +0000743 x = ipod->diroffset % ipod->sector_size;
Dave Chapmand7729772006-12-15 15:52:08 +0000744
Dave Chapman75a11122006-12-14 18:41:03 +0000745 /* Read directory */
Dave Chapman31aa4522007-02-04 11:42:11 +0000746 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
Dave Chapman75a11122006-12-14 18:41:03 +0000747
Dave Chapman31aa4522007-02-04 11:42:11 +0000748 n=ipod_read(ipod, sectorbuf, ipod->sector_size);
Dave Chapman75a11122006-12-14 18:41:03 +0000749 if (n < 0) { return -1; }
750
751 /* Update entries for image 0 */
Dave Chapmand7729772006-12-15 15:52:08 +0000752 int2le(length,sectorbuf+x+16);
753 int2le(0,sectorbuf+x+24);
754 int2le(chksum,sectorbuf+x+28);
Dave Chapman75a11122006-12-14 18:41:03 +0000755
756 /* Write directory */
Dave Chapman31aa4522007-02-04 11:42:11 +0000757 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
758 n=ipod_write(ipod, sectorbuf, ipod->sector_size);
Dave Chapman75a11122006-12-14 18:41:03 +0000759 if (n < 0) { return -1; }
760
761 return 0;
762}
763
Dave Chapman31aa4522007-02-04 11:42:11 +0000764int write_firmware(struct ipod_t* ipod, char* filename, int type)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000765{
766 int length;
767 int i;
Dave Chapmand7729772006-12-15 15:52:08 +0000768 int x;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000769 int n;
770 int infile;
771 int newsize;
772 int bytesavailable;
773 unsigned long chksum=0;
774 unsigned long filechksum=0;
775 unsigned char header[8]; /* Header for .ipod file */
776
Dave Chapmanbbde4452007-06-02 10:10:31 +0000777#ifdef WITH_BOOTOBJS
778 if (type == FILETYPE_INTERNAL) {
779 fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len);
780 length = ipod->bootloader_len;
781 infile = -1;
782 }
783 else
784#endif
785 {
786 /* First check that the input file is the correct type for this ipod. */
787 infile=open(filename,O_RDONLY);
788 if (infile < 0) {
789 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
Dave Chapman31aa4522007-02-04 11:42:11 +0000790 return -1;
791 }
Dave Chapmanbbde4452007-06-02 10:10:31 +0000792
793 if (type==FILETYPE_DOT_IPOD) {
794 n = read(infile,header,8);
795 if (n < 8) {
796 fprintf(stderr,"[ERR] Failed to read header from %s\n",filename);
797 close(infile);
798 return -1;
799 }
800
801 if (memcmp(header+4, ipod->modelname,4)!=0) {
802 fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n",
803 header[4],header[5],header[6],header[7], ipod->modelname);
804 close(infile);
805 return -1;
806 }
807
808 filechksum = be2int(header);
809
810 length = filesize(infile)-8;
811 } else {
812 length = filesize(infile);
Dave Chapman31aa4522007-02-04 11:42:11 +0000813 }
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000814 }
Dave Chapmanbbde4452007-06-02 10:10:31 +0000815
Dave Chapman31aa4522007-02-04 11:42:11 +0000816 newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000817
818 fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n",
819 length,newsize);
820
821 if (newsize > BUFFER_SIZE) {
822 fprintf(stderr,"[ERR] Input file too big for buffer\n");
Dave Chapmanbbde4452007-06-02 10:10:31 +0000823 if (infile >= 0) close(infile);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000824 return -1;
825 }
826
827 /* Check if we have enough space */
828 /* TODO: Check the size of the partition. */
Dave Chapman31aa4522007-02-04 11:42:11 +0000829 if (ipod->nimages > 1) {
830 bytesavailable=ipod->ipod_directory[1].devOffset-ipod->ipod_directory[0].devOffset;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000831 if (bytesavailable < newsize) {
832 fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n");
833
834 /* TODO: Implement image movement */
835 fprintf(stderr,"[ERR] Image movement not yet implemented.\n");
836 close(infile);
837 return -1;
838 }
839 }
840
Dave Chapmanbbde4452007-06-02 10:10:31 +0000841#ifdef WITH_BOOTOBJS
842 if (type == FILETYPE_INTERNAL) {
843 memcpy(sectorbuf,ipod->bootloader,ipod->bootloader_len);
844 }
845 else
846#endif
847 {
848 fprintf(stderr,"[INFO] Reading input file...\n");
849 /* We now know we have enough space, so write it. */
850 n = read(infile,sectorbuf,length);
851 if (n < 0) {
852 fprintf(stderr,"[ERR] Couldn't read input file\n");
853 close(infile);
854 return -1;
855 }
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000856 close(infile);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000857 }
Dave Chapmanbbde4452007-06-02 10:10:31 +0000858
859 /* Pad the data with zeros */
860 memset(sectorbuf+length,0,newsize-length);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000861
Dave Chapmanbdc27ff2007-02-08 18:05:50 +0000862 if (type==FILETYPE_DOT_IPOD) {
Dave Chapman31aa4522007-02-04 11:42:11 +0000863 chksum = ipod->modelnum;
864 for (i = 0; i < length; i++) {
865 /* add 8 unsigned bits but keep a 32 bit sum */
866 chksum += sectorbuf[i];
867 }
868
869 if (chksum == filechksum) {
870 fprintf(stderr,"[INFO] Checksum OK in %s\n",filename);
871 } else {
872 fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename);
873 return -1;
874 }
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000875 }
876
Dave Chapman31aa4522007-02-04 11:42:11 +0000877 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000878 fprintf(stderr,"[ERR] Seek failed\n");
879 return -1;
880 }
881
Dave Chapman31aa4522007-02-04 11:42:11 +0000882 if ((n = ipod_write(ipod,sectorbuf,newsize)) < 0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000883 perror("[ERR] Write failed\n");
884 return -1;
885 }
886
887 if (n < newsize) {
888 fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n"
Dave Chapman3b1119b2007-07-29 20:43:42 +0000889 ,newsize,n);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000890 return -1;
891 }
892 fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n);
893
894 /* Now we need to update the "len", "entryOffset" and "chksum" fields */
895 chksum = 0;
896 for (i = 0; i < length; i++) {
897 /* add 8 unsigned bits but keep a 32 bit sum */
898 chksum += sectorbuf[i];
899 }
900
Dave Chapman31aa4522007-02-04 11:42:11 +0000901 x = ipod->diroffset % ipod->sector_size;
Dave Chapmand7729772006-12-15 15:52:08 +0000902
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000903 /* Read directory */
Dave Chapman31aa4522007-02-04 11:42:11 +0000904 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000905
Dave Chapman31aa4522007-02-04 11:42:11 +0000906 n=ipod_read(ipod, sectorbuf, ipod->sector_size);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000907 if (n < 0) { return -1; }
908
909 /* Update entries for image 0 */
Dave Chapmand7729772006-12-15 15:52:08 +0000910 int2le(length,sectorbuf+x+16);
911 int2le(0,sectorbuf+x+24);
912 int2le(chksum,sectorbuf+x+28);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000913
914 /* Write directory */
Dave Chapman31aa4522007-02-04 11:42:11 +0000915 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
916 n=ipod_write(ipod, sectorbuf, ipod->sector_size);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000917 if (n < 0) { return -1; }
918
919 return 0;
920}
921
Dave Chapman427fff42007-04-13 23:28:20 +0000922int read_firmware(struct ipod_t* ipod, char* filename, int type)
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000923{
924 int length;
925 int i;
926 int outfile;
927 int n;
928 unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/
929 unsigned char header[8]; /* Header for .ipod file */
930
Dave Chapman31aa4522007-02-04 11:42:11 +0000931 if (ipod->ipod_directory[0].entryOffset != 0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000932 /* We have a bootloader... */
Dave Chapman31aa4522007-02-04 11:42:11 +0000933 length = ipod->ipod_directory[0].entryOffset;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000934 } else {
Dave Chapman31aa4522007-02-04 11:42:11 +0000935 length = ipod->ipod_directory[0].len;
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000936 }
937
938 fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length);
939
Dave Chapman31aa4522007-02-04 11:42:11 +0000940 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000941 return -1;
942 }
943
Dave Chapman31aa4522007-02-04 11:42:11 +0000944 i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000945 fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n",
946 length,i);
947
Dave Chapman31aa4522007-02-04 11:42:11 +0000948 if ((n = ipod_read(ipod,sectorbuf,i)) < 0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000949 return -1;
950 }
951
952 if (n < i) {
953 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
954 i,n);
955 return -1;
956 }
957
Dave Chapmancd067552006-12-14 10:16:10 +0000958 outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000959 if (outfile < 0) {
960 fprintf(stderr,"[ERR] Couldn't open file %s\n",filename);
961 return -1;
962 }
963
Dave Chapman427fff42007-04-13 23:28:20 +0000964 if (type == FILETYPE_DOT_IPOD) {
965 chksum = ipod->modelnum;
966 for (i = 0; i < length; i++) {
967 /* add 8 unsigned bits but keep a 32 bit sum */
968 chksum += sectorbuf[i];
969 }
970
971 int2be(chksum,header);
972 memcpy(header+4, ipod->modelname,4);
973
Dave Chapman33bc6f32007-07-29 21:34:28 +0000974 n = write(outfile,header,8);
975 if (n != 8) {
976 fprintf(stderr,"[ERR] Write error - %d\n",n);
977 }
Dave Chapman427fff42007-04-13 23:28:20 +0000978 }
979
Dave Chapman33bc6f32007-07-29 21:34:28 +0000980 n = write(outfile,sectorbuf,length);
981 if (n != length) {
982 fprintf(stderr,"[ERR] Write error - %d\n",n);
983 }
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000984 close(outfile);
985
986 return 0;
987}
988
Dave Chapman31aa4522007-02-04 11:42:11 +0000989int read_directory(struct ipod_t* ipod)
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000990{
991 int n;
Dave Chapmand7729772006-12-15 15:52:08 +0000992 int x;
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000993 unsigned char* p;
Dave Chapman31aa4522007-02-04 11:42:11 +0000994 unsigned short version;
Dave Chapman4b7e1e02006-12-13 09:02:18 +0000995
Dave Chapman1026c0f2007-02-06 21:00:56 +0000996 ipod->nimages=0;
997
Dave Chapmanc5e30e12006-12-14 01:24:05 +0000998 /* Read firmware partition header (first 512 bytes of disk - but
999 let's read a whole sector) */
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001000
Dave Chapman31aa4522007-02-04 11:42:11 +00001001 if (ipod_seek(ipod, ipod->start) < 0) {
1002 fprintf(stderr,"[ERR] Seek to 0x%08x in read_directory() failed.\n",
1003 (unsigned int)(ipod->start));
Dave Chapmand7729772006-12-15 15:52:08 +00001004 return -1;
1005 }
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001006
Dave Chapman31aa4522007-02-04 11:42:11 +00001007 n=ipod_read(ipod, sectorbuf, ipod->sector_size);
Dave Chapmand7729772006-12-15 15:52:08 +00001008 if (n < 0) {
Dave Chapman31aa4522007-02-04 11:42:11 +00001009 fprintf(stderr,"[ERR] ipod_read(ipod,buf,0x%08x) failed in read_directory()\n", ipod->sector_size);
Dave Chapmand7729772006-12-15 15:52:08 +00001010 return -1;
1011 }
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001012
1013 if (memcmp(sectorbuf,apple_stop_sign,sizeof(apple_stop_sign))!=0) {
Dave Chapman1026c0f2007-02-06 21:00:56 +00001014 fprintf(stderr,"[ERR] Firmware partition doesn't contain Apple copyright, aborting.\n");
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001015 return -1;
1016 }
1017
1018 if (memcmp(sectorbuf+0x100,"]ih[",4)!=0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001019 fprintf(stderr,"[ERR] Bad firmware directory\n");
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001020 return -1;
1021 }
1022
Dave Chapman31aa4522007-02-04 11:42:11 +00001023 version = le2ushort(sectorbuf+0x10a);
1024 if ((version != 2) && (version != 3)) {
1025 fprintf(stderr,"[ERR] Unknown firmware format version %04x\n",
1026 version);
1027 }
1028 ipod->diroffset=le2int(sectorbuf+0x104) + 0x200;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001029
Dave Chapmand7729772006-12-15 15:52:08 +00001030 /* diroffset may not be sector-aligned */
Dave Chapman31aa4522007-02-04 11:42:11 +00001031 x = ipod->diroffset % ipod->sector_size;
Dave Chapmand7729772006-12-15 15:52:08 +00001032
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001033 /* Read directory */
Dave Chapman31aa4522007-02-04 11:42:11 +00001034 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) {
1035 fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset);
Dave Chapmand7729772006-12-15 15:52:08 +00001036 return -1;
1037 }
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001038
Dave Chapman31aa4522007-02-04 11:42:11 +00001039 n=ipod_read(ipod, sectorbuf, ipod->sector_size);
Dave Chapmand7729772006-12-15 15:52:08 +00001040 if (n < 0) {
1041 fprintf(stderr,"[ERR] Read of directory failed.\n");
1042 return -1;
1043 }
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001044
Dave Chapmand7729772006-12-15 15:52:08 +00001045 p = sectorbuf + x;
Dave Chapman564d2492007-08-03 02:12:32 +00001046
1047 /* A hack to detect 2nd gen Nanos - maybe there is a better way? */
1048 if (p[0] == 0)
1049 {
1050 n=ipod_read(ipod, sectorbuf, ipod->sector_size);
1051 if (n < 0) {
1052 fprintf(stderr,"[ERR] Read of directory failed.\n");
1053 return -1;
1054 }
1055 p = sectorbuf;
1056 }
1057
Dave Chapman31aa4522007-02-04 11:42:11 +00001058 while ((ipod->nimages < MAX_IMAGES) && (p < (sectorbuf + x + 400)) &&
Dave Chapman564d2492007-08-03 02:12:32 +00001059 ((memcmp(p,"!ATA",4)==0) || (memcmp(p,"DNAN",4)==0))) {
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001060 p+=4;
1061 if (memcmp(p,"soso",4)==0) {
Dave Chapman31aa4522007-02-04 11:42:11 +00001062 ipod->ipod_directory[ipod->nimages].ftype=FTYPE_OSOS;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001063 } else if (memcmp(p,"crsr",4)==0) {
Dave Chapman31aa4522007-02-04 11:42:11 +00001064 ipod->ipod_directory[ipod->nimages].ftype=FTYPE_RSRC;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001065 } else if (memcmp(p,"dpua",4)==0) {
Dave Chapman31aa4522007-02-04 11:42:11 +00001066 ipod->ipod_directory[ipod->nimages].ftype=FTYPE_AUPD;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001067 } else if (memcmp(p,"ebih",4)==0) {
Dave Chapman31aa4522007-02-04 11:42:11 +00001068 ipod->ipod_directory[ipod->nimages].ftype=FTYPE_HIBE;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001069 } else {
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001070 fprintf(stderr,"[ERR] Unknown image type %c%c%c%c\n",
1071 p[0],p[1],p[2],p[3]);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001072 }
1073 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001074 ipod->ipod_directory[ipod->nimages].id=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001075 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001076 ipod->ipod_directory[ipod->nimages].devOffset=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001077 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001078 ipod->ipod_directory[ipod->nimages].len=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001079 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001080 ipod->ipod_directory[ipod->nimages].addr=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001081 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001082 ipod->ipod_directory[ipod->nimages].entryOffset=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001083 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001084 ipod->ipod_directory[ipod->nimages].chksum=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001085 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001086 ipod->ipod_directory[ipod->nimages].vers=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001087 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001088 ipod->ipod_directory[ipod->nimages].loadAddr=le2int(p);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001089 p+=4;
Dave Chapman31aa4522007-02-04 11:42:11 +00001090 ipod->nimages++;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001091 }
Dave Chapman31aa4522007-02-04 11:42:11 +00001092
1093 if ((ipod->nimages > 1) && (version==2)) {
1094 /* The 3g firmware image doesn't appear to have a version, so
1095 let's make one up... Note that this is never written back to the
1096 ipod, so it's OK to do. */
1097
1098 if (ipod->ipod_directory[0].vers == 0) { ipod->ipod_directory[0].vers = 3; }
1099
1100 ipod->fwoffset = ipod->start;
1101 } else {
1102 ipod->fwoffset = ipod->start + ipod->sector_size;
1103 }
1104
1105 return 0;
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001106}
1107
Dave Chapman31aa4522007-02-04 11:42:11 +00001108int list_images(struct ipod_t* ipod)
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001109{
1110 int i;
1111
Dave Chapman75a11122006-12-14 18:41:03 +00001112 if (verbose) {
1113 printf(" Type id devOffset len addr entryOffset chksum vers loadAddr devOffset+len\n");
Dave Chapman31aa4522007-02-04 11:42:11 +00001114 for (i = 0 ; i < ipod->nimages; i++) {
Dave Chapman75a11122006-12-14 18:41:03 +00001115 printf("%d - %s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",i,
Dave Chapman31aa4522007-02-04 11:42:11 +00001116 ftypename[ipod->ipod_directory[i].ftype],
1117 ipod->ipod_directory[i].id,
1118 ipod->ipod_directory[i].devOffset,
1119 ipod->ipod_directory[i].len,
1120 ipod->ipod_directory[i].addr,
1121 ipod->ipod_directory[i].entryOffset,
1122 ipod->ipod_directory[i].chksum,
1123 ipod->ipod_directory[i].vers,
1124 ipod->ipod_directory[i].loadAddr,
1125 ipod->ipod_directory[i].devOffset+((ipod->ipod_directory[i].len+ipod->sector_size-1)&~(ipod->sector_size-1)));
Dave Chapman75a11122006-12-14 18:41:03 +00001126 }
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001127 }
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001128
1129 printf("\n");
1130 printf("Listing firmware partition contents:\n");
1131 printf("\n");
1132
Dave Chapman31aa4522007-02-04 11:42:11 +00001133 for (i = 0 ; i < ipod->nimages; i++) {
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001134 printf("Image %d:\n",i+1);
Dave Chapman31aa4522007-02-04 11:42:11 +00001135 switch(ipod->ipod_directory[i].ftype) {
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001136 case FTYPE_OSOS:
Dave Chapman31aa4522007-02-04 11:42:11 +00001137 if (ipod->ipod_directory[i].entryOffset==0) {
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001138 printf(" Main firmware - %d bytes\n",
Dave Chapman31aa4522007-02-04 11:42:11 +00001139 ipod->ipod_directory[i].len);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001140 } else {
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001141 printf(" Main firmware - %d bytes\n",
Dave Chapman31aa4522007-02-04 11:42:11 +00001142 ipod->ipod_directory[i].entryOffset);
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001143 printf(" Third-party bootloader - %d bytes\n",
Dave Chapman31aa4522007-02-04 11:42:11 +00001144 ipod->ipod_directory[i].len-ipod->ipod_directory[i].entryOffset);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001145 }
1146 break;
1147 default:
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001148 printf(" %s - %d bytes\n",
Dave Chapman31aa4522007-02-04 11:42:11 +00001149 ftypename[ipod->ipod_directory[i].ftype],
1150 ipod->ipod_directory[i].len);
Dave Chapman4b7e1e02006-12-13 09:02:18 +00001151 }
1152 }
1153 printf("\n");
1154
1155 return 0;
1156}
1157
Dave Chapman31aa4522007-02-04 11:42:11 +00001158int getmodel(struct ipod_t* ipod, int ipod_version)
Dave Chapman57b84b62006-12-17 23:00:15 +00001159{
1160 switch (ipod_version) {
Dave Chapman6a20def2007-07-26 20:21:11 +00001161 case 0x01:
1162 ipod->modelstr="1st or 2nd Generation";
1163 ipod->modelnum = 19;
1164 ipod->modelname = "1g2g";
1165 ipod->targetname = "ipod1g2g";
1166#ifdef WITH_BOOTOBJS
1167 ipod->bootloader = ipod1g2g;
1168 ipod->bootloader_len = LEN_ipod1g2g;
1169#endif
1170 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001171 case 0x02:
1172 ipod->modelstr="3rd Generation";
1173 ipod->modelnum = 7;
1174 ipod->modelname = "ip3g";
Dave Chapmana6d68bd2007-02-10 20:09:23 +00001175 ipod->targetname = "ipod3g";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001176#ifdef WITH_BOOTOBJS
1177 ipod->bootloader = ipod3g;
1178 ipod->bootloader_len = LEN_ipod3g;
1179#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001180 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001181 case 0x40:
1182 ipod->modelstr="1st Generation Mini";
1183 ipod->modelnum = 9;
1184 ipod->modelname = "mini";
Dominik Wengerac3db332007-08-24 08:10:19 +00001185 ipod->targetname = "ipodmini1g";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001186#ifdef WITH_BOOTOBJS
1187 ipod->bootloader = ipodmini;
1188 ipod->bootloader_len = LEN_ipodmini;
1189#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001190 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001191 case 0x50:
1192 ipod->modelstr="4th Generation";
1193 ipod->modelnum = 8;
1194 ipod->modelname = "ip4g";
Dominik Wengerac3db332007-08-24 08:10:19 +00001195 ipod->targetname = "ipod4gray";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001196#ifdef WITH_BOOTOBJS
1197 ipod->bootloader = ipod4g;
1198 ipod->bootloader_len = LEN_ipod4g;
1199#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001200 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001201 case 0x60:
1202 ipod->modelstr="Photo/Color";
1203 ipod->modelnum = 3;
1204 ipod->modelname = "ipco";
Dave Chapmana6d68bd2007-02-10 20:09:23 +00001205 ipod->targetname = "ipodcolor";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001206#ifdef WITH_BOOTOBJS
1207 ipod->bootloader = ipodcolor;
1208 ipod->bootloader_len = LEN_ipodcolor;
1209#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001210 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001211 case 0x70:
1212 ipod->modelstr="2nd Generation Mini";
1213 ipod->modelnum = 11;
1214 ipod->modelname = "mn2g";
Dave Chapmana6d68bd2007-02-10 20:09:23 +00001215 ipod->targetname = "ipodmini2g";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001216#ifdef WITH_BOOTOBJS
1217 ipod->bootloader = ipodmini2g;
1218 ipod->bootloader_len = LEN_ipodmini2g;
1219#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001220 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001221 case 0xc0:
1222 ipod->modelstr="1st Generation Nano";
1223 ipod->modelnum = 4;
1224 ipod->modelname = "nano";
Dave Chapmana6d68bd2007-02-10 20:09:23 +00001225 ipod->targetname = "ipodnano";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001226#ifdef WITH_BOOTOBJS
1227 ipod->bootloader = ipodnano;
1228 ipod->bootloader_len = LEN_ipodnano;
1229#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001230 break;
Dave Chapman31aa4522007-02-04 11:42:11 +00001231 case 0xb0:
1232 ipod->modelstr="Video (aka 5th Generation)";
1233 ipod->modelnum = 5;
1234 ipod->modelname = "ipvd";
Dave Chapmana6d68bd2007-02-10 20:09:23 +00001235 ipod->targetname = "ipodvideo";
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001236#ifdef WITH_BOOTOBJS
1237 ipod->bootloader = ipodvideo;
1238 ipod->bootloader_len = LEN_ipodvideo;
1239#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001240 break;
Dave Chapman564d2492007-08-03 02:12:32 +00001241 case 0x100:
1242 ipod->modelstr="2nd Generation Nano";
1243 ipod->modelnum = 0;
1244 ipod->targetname = NULL;
1245#ifdef WITH_BOOTOBJS
1246 ipod->bootloader = NULL;
1247 ipod->bootloader_len = 0;
1248#endif
1249 break;
Dave Chapman57b84b62006-12-17 23:00:15 +00001250 default:
Dave Chapman31aa4522007-02-04 11:42:11 +00001251 ipod->modelname = NULL;
1252 ipod->modelnum = 0;
Dave Chapmana6d68bd2007-02-10 20:09:23 +00001253 ipod->targetname = NULL;
Dave Chapmanbdc27ff2007-02-08 18:05:50 +00001254#ifdef WITH_BOOTOBJS
1255 ipod->bootloader = NULL;
1256 ipod->bootloader_len = 0;
1257#endif
Dave Chapman57b84b62006-12-17 23:00:15 +00001258 return -1;
1259 }
1260 return 0;
1261}
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001262
Dave Chapman31aa4522007-02-04 11:42:11 +00001263int ipod_scan(struct ipod_t* ipod)
Dave Chapman57b84b62006-12-17 23:00:15 +00001264{
1265 int i;
1266 int n = 0;
Dave Chapman57b84b62006-12-17 23:00:15 +00001267 int ipod_version;
Dave Chapman1026c0f2007-02-06 21:00:56 +00001268 char last_ipod[4096];
Dave Chapmanc5e30e12006-12-14 01:24:05 +00001269
Dave Chapman57b84b62006-12-17 23:00:15 +00001270 printf("[INFO] Scanning disk devices...\n");
1271
Dave Chapman6e641e82007-02-05 18:29:39 +00001272 for (i = 0; i <= 25 ; i++) {
Dave Chapman57b84b62006-12-17 23:00:15 +00001273#ifdef __WIN32__
Dave Chapman31aa4522007-02-04 11:42:11 +00001274 sprintf(ipod->diskname,"\\\\.\\PhysicalDrive%d",i);
Dave Chapman57b84b62006-12-17 23:00:15 +00001275#elif defined(linux) || defined (__linux)
Dave Chapman31aa4522007-02-04 11:42:11 +00001276 sprintf(ipod->diskname,"/dev/sd%c",'a'+i);
Dave Chapman57b84b62006-12-17 23:00:15 +00001277#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
1278 || defined(__bsdi__) || defined(__DragonFly__)
Dave Chapman31aa4522007-02-04 11:42:11 +00001279 sprintf(ipod->diskname,"/dev/da%d",i);
Dave Chapman57b84b62006-12-17 23:00:15 +00001280#elif defined(__APPLE__) && defined(__MACH__)
Dave Chapman31aa4522007-02-04 11:42:11 +00001281 sprintf(ipod->diskname,"/dev/disk%d",i);
Dave Chapman57b84b62006-12-17 23:00:15 +00001282#else
1283 #error No disk paths defined for this platform
1284#endif
Dave Chapman31aa4522007-02-04 11:42:11 +00001285 if (ipod_open(ipod, 1) < 0) {
Dave Chapman57b84b62006-12-17 23:00:15 +00001286 continue;
1287 }
1288
Dave Chapman31aa4522007-02-04 11:42:11 +00001289 if (read_partinfo(ipod,1) < 0) {
Dave Chapman57b84b62006-12-17 23:00:15 +00001290 continue;
1291 }
1292
Dave Chapman31aa4522007-02-04 11:42:11 +00001293 if ((ipod->pinfo[0].start==0) || (ipod->pinfo[0].type != 0)) {
Dave Chapman57b84b62006-12-17 23:00:15 +00001294 continue;
1295 }
1296
Dave Chapman31aa4522007-02-04 11:42:11 +00001297 if (read_directory(ipod) < 0) {
Dave Chapman57b84b62006-12-17 23:00:15 +00001298 continue;
1299 }
1300
Dave Chapman31aa4522007-02-04 11:42:11 +00001301 ipod_version=(ipod->ipod_directory[0].vers>>8);
1302 if (getmodel(ipod,ipod_version) < 0) {
Dave Chapman57b84b62006-12-17 23:00:15 +00001303 continue;
1304 }
1305
1306#ifdef __WIN32__
Dave Chapman6e641e82007-02-05 18:29:39 +00001307 printf("[INFO] Ipod found - %s (\"%s\") - disk device %d\n",
1308 ipod->modelstr,ipod->macpod ? "macpod" : "winpod",i);
Dave Chapman57b84b62006-12-17 23:00:15 +00001309#else
Dave Chapman6e641e82007-02-05 18:29:39 +00001310 printf("[INFO] Ipod found - %s (\"%s\") - %s\n",
1311 ipod->modelstr,ipod->macpod ? "macpod" : "winpod",ipod->diskname);
Dave Chapman57b84b62006-12-17 23:00:15 +00001312#endif
1313 n++;
Dave Chapman1026c0f2007-02-06 21:00:56 +00001314 strcpy(last_ipod,ipod->diskname);
Dave Chapmandd50c862007-02-06 22:48:30 +00001315 ipod_close(ipod);
Dave Chapman57b84b62006-12-17 23:00:15 +00001316 }
1317
Dave Chapmaneca52222007-02-08 21:31:38 +00001318 if (n==1) {
Dave Chapman1026c0f2007-02-06 21:00:56 +00001319 /* Remember the disk name */
1320 strcpy(ipod->diskname,last_ipod);
Dave Chapman57b84b62006-12-17 23:00:15 +00001321 }
Dave Chapman1026c0f2007-02-06 21:00:56 +00001322 return n;
Dave Chapman57b84b62006-12-17 23:00:15 +00001323}
Dave Chapman35735c662007-07-27 20:51:36 +00001324
1325static void put_int32le(uint32_t x, unsigned char* p)
1326{
1327 p[0] = x & 0xff;
1328 p[1] = (x >> 8) & 0xff;
1329 p[2] = (x >> 16) & 0xff;
1330 p[3] = (x >> 24) & 0xff;
1331}
1332
1333int write_dos_partition_table(struct ipod_t* ipod)
1334{
1335 unsigned char* p;
1336 int i, n;
1337 uint32_t type;
1338
1339 /* Only support 512-byte sectors at the moment */
1340 if ( ipod->sector_size != 512 )
1341 {
1342 fprintf(stderr,"[ERR] Only ipods with 512 bytes per sector are supported.\n");
1343 return -1;
1344 }
1345
1346 /* Firstly zero the entire MBR */
1347 memset(sectorbuf, 0, ipod->sector_size);
1348
1349 /* Now add the partition info */
1350 for (i=0; i < 4 ; i++)
1351 {
1352 p = sectorbuf + 0x1be + i*16;
1353
1354 /* Ensure first partition is type 0, and second is 0xb */
1355 if (i==0) { type = 0; }
1356 else if (i==1) { type = 0xb; }
1357 else { type = ipod->pinfo[i].type; }
1358
1359 put_int32le(type, p + 4);
1360 put_int32le(ipod->pinfo[i].start, p + 8);
1361 put_int32le(ipod->pinfo[i].size, p + 12);
1362 }
1363
1364 /* Finally add the magic */
1365 sectorbuf[0x1fe] = 0x55;
1366 sectorbuf[0x1ff] = 0xaa;
1367
1368 if (ipod_seek(ipod, 0) < 0) {
1369 fprintf(stderr,"[ERR] Seek failed writing MBR\n");
1370 return -1;
1371 }
1372
1373 /* Write MBR */
1374 if ((n = ipod_write(ipod, sectorbuf, ipod->sector_size)) < 0) {
1375 perror("[ERR] Write failed\n");
1376 return -1;
1377 }
1378
1379 return 0;
1380}
Dave Chapman6e797152007-09-08 23:27:49 +00001381
1382#ifndef RBUTIL
1383
1384static inline uint32_t getuint32le(unsigned char* buf)
1385{
1386 int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
1387
1388 return res;
1389}
1390
1391/* testMarker and GetSecurityBlockKey based on code from BadBlocks and
1392 Kingstone, posted at http://ipodlinux.org/Flash_Decryption
1393
1394*/
1395
1396static bool testMarker(int marker)
1397{
1398 int mask, decrypt, temp1, temp2;
1399
1400 mask = (marker&0xff)|((marker&0xff)<<8)|((marker&0xff)<<16)|((marker&0xff)<<24);
1401 decrypt = marker ^ mask;
1402 temp1=(int)((unsigned int)decrypt>>24);
1403 temp2=decrypt<<8;
1404
1405 if (temp1==0)
1406 return false;
1407
1408 temp2=(int)((unsigned int)temp2>>24);
1409 decrypt=decrypt<<16;
1410 decrypt=(int)((unsigned int)decrypt>>24);
1411
1412 if ((temp1 < temp2) && (temp2 < decrypt))
1413 {
1414 temp1 = temp1 & 0xf;
1415 temp2 = temp2 & 0xf;
1416 decrypt = decrypt & 0xf;
1417
1418 if ((temp1 > temp2) && (temp2 > decrypt) && (decrypt != 0))
1419 {
1420 return true;
1421 }
1422 }
1423 return false;
1424}
1425
1426static int GetSecurityBlockKey(unsigned char *data, unsigned char* this_key)
1427{
1428 int constant = 0x54c3a298;
1429 int key=0;
1430 int nkeys = 0;
1431 int aMarker=0;
1432 int pos=0;
1433 int c, count;
1434 int temp1;
1435 static const int offset[8]={0x5,0x25,0x6f,0x69,0x15,0x4d,0x40,0x34};
1436
1437 for (c = 0; c < 8; c++)
1438 {
1439 pos = offset[c]*4;
1440 aMarker = getuint32le(data + pos);
1441
1442 if (testMarker(aMarker))
1443 {
1444 if (c<7)
1445 pos =(offset[c+1]*4)+4;
1446 else
1447 pos =(offset[0]*4)+4;
1448
1449 key=0;
1450
1451 temp1=aMarker;
1452
1453 for (count=0;count<2;count++){
1454 int word = getuint32le(data + pos);
1455 temp1 = aMarker;
1456 temp1 = temp1^word;
1457 temp1 = temp1^constant;
1458 key = temp1;
1459 pos = pos+4;
1460 }
1461 int r1=0x6f;
1462 int r2=0;
1463 int r12;
1464 int r14;
1465 unsigned int r_tmp;
1466
1467 for (count=2;count<128;count=count+2){
1468 r2=getuint32le(data+count*4);
1469 r12=getuint32le(data+(count*4)+4);
1470 r_tmp=(unsigned int)r12>>16;
1471 r14=r2 | ((int)r_tmp);
1472 r2=r2&0xffff;
1473 r2=r2 | r12;
1474 r1=r1^r14;
1475 r1=r1+r2;
1476 }
1477 key=key^r1;
1478
1479 // Invert key, little endian
1480 this_key[0] = key & 0xff;
1481 this_key[1] = (key >> 8) & 0xff;
1482 this_key[2] = (key >> 16) & 0xff;
1483 this_key[3] = (key >> 24) & 0xff;
1484 nkeys++;
1485 }
1486 }
1487 return nkeys;
1488}
1489
1490static int find_key(struct ipod_t* ipod, int aupd, unsigned char* key)
1491{
1492 int n;
1493
1494 /* Firstly read the security block and find the RC4 key. This is
1495 in the sector preceeding the AUPD image. */
1496
1497 fprintf(stderr, "[INFO] Reading security block at offset 0x%08x\n",ipod->ipod_directory[aupd].devOffset-ipod->sector_size);
1498 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset-ipod->sector_size) < 0) {
1499 return -1;
1500 }
1501
1502 if ((n = ipod_read(ipod, sectorbuf, 512)) < 0) {
1503 return -1;
1504 }
1505
1506 n = GetSecurityBlockKey(sectorbuf, key);
1507
1508 if (n != 1)
1509 {
1510 fprintf(stderr, "[ERR] %d keys found in security block, can not continue\n",n);
1511 return -1;
1512 }
1513
1514 return 0;
1515}
1516
1517int read_aupd(struct ipod_t* ipod, char* filename)
1518{
1519 int length;
1520 int i;
1521 int outfile;
1522 int n;
1523 int aupd;
1524 struct rc4_key_t rc4;
1525 unsigned char key[4];
1526 unsigned long chksum=0;
1527
1528 aupd = 0;
1529 while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD))
1530 {
1531 aupd++;
1532 }
1533
1534 if (aupd == ipod->nimages)
1535 {
1536 fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n");
1537 return -1;
1538 }
1539
1540 length = ipod->ipod_directory[aupd].len;
1541
1542 fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length);
1543
1544 if (find_key(ipod, aupd, key) < 0)
1545 {
1546 return -1;
1547 }
1548
1549 fprintf(stderr, "[INFO] Decrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]);
1550
1551 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) {
1552 return -1;
1553 }
1554
1555 i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1);
1556
1557 if ((n = ipod_read(ipod,sectorbuf,i)) < 0) {
1558 return -1;
1559 }
1560
1561 if (n < i) {
1562 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
1563 i,n);
1564 return -1;
1565 }
1566
1567 /* Perform the decryption - this is standard (A)RC4 */
1568 matrixArc4Init(&rc4, key, 4);
1569 matrixArc4(&rc4, sectorbuf, sectorbuf, length);
1570
1571 chksum = 0;
1572 for (i = 0; i < (int)length; i++) {
1573 /* add 8 unsigned bits but keep a 32 bit sum */
1574 chksum += sectorbuf[i];
1575 }
1576
1577 if (chksum != ipod->ipod_directory[aupd].chksum)
1578 {
1579 fprintf(stderr,"[ERR] Decryption failed - checksum error\n");
1580 return -1;
1581 }
1582 fprintf(stderr,"[INFO] Decrypted OK (checksum matches header)\n");
1583
1584 outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
1585 if (outfile < 0) {
1586 fprintf(stderr,"[ERR] Couldn't open file %s\n",filename);
1587 return -1;
1588 }
1589
1590 n = write(outfile,sectorbuf,length);
1591 if (n != length) {
1592 fprintf(stderr,"[ERR] Write error - %d\n",n);
1593 }
1594 close(outfile);
1595
1596 return 0;
1597}
1598
1599int write_aupd(struct ipod_t* ipod, char* filename)
1600{
1601 unsigned int length;
1602 int i;
1603 int x;
1604 int n;
1605 int infile;
1606 int newsize;
1607 int aupd;
1608 unsigned long chksum=0;
1609 struct rc4_key_t rc4;
1610 unsigned char key[4];
1611
1612 /* First check that the input file is the correct type for this ipod. */
1613 infile=open(filename,O_RDONLY);
1614 if (infile < 0) {
1615 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
1616 return -1;
1617 }
1618
1619 length = filesize(infile);
1620 newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
1621
1622 fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n",
1623 length,newsize);
1624
1625 if (newsize > BUFFER_SIZE) {
1626 fprintf(stderr,"[ERR] Input file too big for buffer\n");
1627 if (infile >= 0) close(infile);
1628 return -1;
1629 }
1630
1631 /* Find aupd image number */
1632 aupd = 0;
1633 while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD))
1634 {
1635 aupd++;
1636 }
1637
1638 if (aupd == ipod->nimages)
1639 {
1640 fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n");
1641 return -1;
1642 }
1643
1644 if (length != ipod->ipod_directory[aupd].len)
1645 {
1646 fprintf(stderr,"[ERR] AUPD image (%d bytes) differs in size to %s (%d bytes).\n",
1647 ipod->ipod_directory[aupd].len, filename, length);
1648 return -1;
1649 }
1650
1651 if (find_key(ipod, aupd, key) < 0)
1652 {
1653 return -1;
1654 }
1655
1656 fprintf(stderr, "[INFO] Encrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]);
1657
1658 /* We now know we have enough space, so write it. */
1659
1660 fprintf(stderr,"[INFO] Reading input file...\n");
1661 n = read(infile,sectorbuf,length);
1662 if (n < 0) {
1663 fprintf(stderr,"[ERR] Couldn't read input file\n");
1664 close(infile);
1665 return -1;
1666 }
1667 close(infile);
1668
1669 /* Pad the data with zeros */
1670 memset(sectorbuf+length,0,newsize-length);
1671
1672 /* Calculate the new checksum (before we encrypt) */
1673 chksum = 0;
1674 for (i = 0; i < (int)length; i++) {
1675 /* add 8 unsigned bits but keep a 32 bit sum */
1676 chksum += sectorbuf[i];
1677 }
1678
1679 /* Perform the encryption - this is standard (A)RC4 */
1680 matrixArc4Init(&rc4, key, 4);
1681 matrixArc4(&rc4, sectorbuf, sectorbuf, length);
1682
1683 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) {
1684 fprintf(stderr,"[ERR] Seek failed\n");
1685 return -1;
1686 }
1687
1688 if ((n = ipod_write(ipod,sectorbuf,newsize)) < 0) {
1689 perror("[ERR] Write failed\n");
1690 return -1;
1691 }
1692
1693 if (n < newsize) {
1694 fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n"
1695 ,newsize,n);
1696 return -1;
1697 }
1698 fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n);
1699
1700 x = ipod->diroffset % ipod->sector_size;
1701
1702 /* Read directory */
1703 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
1704
1705 n=ipod_read(ipod, sectorbuf, ipod->sector_size);
1706 if (n < 0) { return -1; }
1707
1708 /* Update checksum */
1709 fprintf(stderr,"[INFO] Updating checksum to 0x%08x (was 0x%08x)\n",(unsigned int)chksum,le2int(sectorbuf + x + aupd*40 + 28));
1710 int2le(chksum,sectorbuf+x+aupd*40+28);
1711
1712 /* Write directory */
1713 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
1714 n=ipod_write(ipod, sectorbuf, ipod->sector_size);
1715 if (n < 0) { return -1; }
1716
1717 return 0;
1718}
1719
1720#endif