summaryrefslogtreecommitdiff
path: root/utils/sansapatcher
diff options
context:
space:
mode:
Diffstat (limited to 'utils/sansapatcher')
-rw-r--r--utils/sansapatcher/Makefile52
-rw-r--r--utils/sansapatcher/README36
-rw-r--r--utils/sansapatcher/main.c420
-rw-r--r--utils/sansapatcher/parttypes.h109
-rw-r--r--utils/sansapatcher/sansaio-posix.c157
-rw-r--r--utils/sansapatcher/sansaio-win32.c217
-rw-r--r--utils/sansapatcher/sansaio.h88
-rw-r--r--utils/sansapatcher/sansapatcher.c975
-rw-r--r--utils/sansapatcher/sansapatcher.h67
-rw-r--r--utils/sansapatcher/sansapatcher.manifest13
-rw-r--r--utils/sansapatcher/sansapatcher.pro38
-rw-r--r--utils/sansapatcher/sansapatcher.rc1
12 files changed, 2173 insertions, 0 deletions
diff --git a/utils/sansapatcher/Makefile b/utils/sansapatcher/Makefile
new file mode 100644
index 0000000000..85c36c6d14
--- /dev/null
+++ b/utils/sansapatcher/Makefile
@@ -0,0 +1,52 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7
8TARGET_DIR ?= $(shell pwd)/
9CFLAGS += -Wall -W -D_LARGEFILE64_SOURCE
10
11OUTPUT = sansapatcher
12
13# inputs
14LIBSOURCES := sansapatcher.c sansaio-posix.c sansaio-win32.c
15SOURCES := main.c
16# additional link dependencies for the standalone executable
17EXTRADEPS :=
18
19# Releases of sansapatcher are created with "make RELEASE=1". This
20# enables BOOTOBJS and uses the VERSION string defined in main.c
21ifdef RELEASE
22CFLAGS += -DRELEASE
23BOOTOBJS=1
24endif
25
26ifdef WITH_BOOTOBJS
27BOOTSRC = bootimg_c200.c bootimg_e200.c
28SOURCES += $(BOOTSRC)
29CFLAGS += -DWITH_BOOTOBJS
30endif
31
32include ../libtools.make
33
34# find out if we need to link the manifest resource.
35# Since libtools.make sets up BINARY we check it for the file extension .exe.
36ifeq ($(findstring exe,$(BINARY)),exe)
37$(BINARY): $(OBJDIR)sansapatcher-rc.o
38endif
39
40$(OBJDIR)main.o: $(BOOTSRC)
41$(OBJDIR)sansapatcher-rc.o: sansapatcher.rc sansapatcher.manifest
42 @echo WINDRES $(notdir $<)
43 $(SILENT)$(CROSS)$(WINDRES) -i sansapatcher.rc -o $@
44
45bootimg_c200.c: firmware.mi4 $(BIN2C)
46 @echo BIN2C $<
47 $(SILENT)$(BIN2C) $< $*
48
49bootimg_e200.c: PP5022.mi4 $(BIN2C)
50 @echo BIN2C $< $*
51 $(SILENT)$(BIN2C) $< $*
52
diff --git a/utils/sansapatcher/README b/utils/sansapatcher/README
new file mode 100644
index 0000000000..c62f676110
--- /dev/null
+++ b/utils/sansapatcher/README
@@ -0,0 +1,36 @@
1sansapatcher
2------------
3
4To compile sansapatcher, you need both the C200 and E200 Rockbox
5bootloaders. The latest bootloaders can always be found here:
6
7http://download.rockbox.org/bootloader/sandisk-sansa/e200/PP5022.mi4
8http://download.rockbox.org/bootloader/sandisk-sansa/c200/firmware.mi4
9
10Place both these files in the sansapatcher source directory, and type "make".
11
12
13Building your own bootloaders
14-----------------------------
15
16If you would like to compile the bootloaders yourself, they are the output of
17running the "Bootloader" build for the E200 and C200 targets.
18
19NOTE: Unless you know what you are doing, it is recommended that you
20 use the official pre-built binary bootloaders linked to above.
21 Bootloaders compiled from current Rockbox SVN are untested and
22 may contain bugs preventing you from starting the device (or
23 worse...).
24
25In the Rockbox source directory, do:
26
27mkdir build-e200-bootloader
28cd build-e200-bootloader
29../tools/configure
30[Select E200, then B for bootloader]
31make
32
33This will create PP5022.mi4 which you should copy to the sansapatcher
34build directory.
35
36A similar process for the C200 will create firmware.mi4.
diff --git a/utils/sansapatcher/main.c b/utils/sansapatcher/main.c
new file mode 100644
index 0000000000..d12e33c22b
--- /dev/null
+++ b/utils/sansapatcher/main.c
@@ -0,0 +1,420 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006-2007 Dave Chapman
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include <stdio.h>
23#include <unistd.h>
24#include <fcntl.h>
25#include <string.h>
26#include <stdlib.h>
27#include <inttypes.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30
31#include "sansapatcher.h"
32#include "sansaio.h"
33#include "parttypes.h"
34#ifdef WITH_BOOTOBJS
35#include "bootimg_c200.h"
36#include "bootimg_e200.h"
37#endif
38
39#ifndef VERSION
40#define VERSION "0.8 with v6.0 bootloaders"
41#endif
42
43enum {
44 NONE,
45 INSTALL,
46 INTERACTIVE,
47 SHOW_INFO,
48 LIST_IMAGES,
49 DELETE_BOOTLOADER,
50 ADD_BOOTLOADER,
51 READ_FIRMWARE,
52 WRITE_FIRMWARE,
53 READ_PARTITION,
54 WRITE_PARTITION,
55 UPDATE_OF,
56 UPDATE_PPBL
57};
58
59static void print_usage(void)
60{
61 fprintf(stderr,"Usage: sansapatcher --scan\n");
62#ifdef __WIN32__
63 fprintf(stderr," or sansapatcher [DISKNO] [action]\n");
64#else
65 fprintf(stderr," or sansapatcher [device] [action]\n");
66#endif
67 fprintf(stderr,"\n");
68 fprintf(stderr,"Where [action] is one of the following options:\n");
69 fprintf(stderr," --install\n");
70 fprintf(stderr," -l, --list\n");
71 fprintf(stderr," -rf, --read-firmware filename.mi4\n");
72 fprintf(stderr," -a, --add-bootloader filename.mi4\n");
73 fprintf(stderr," -d, --delete-bootloader\n");
74 fprintf(stderr," -of --update-original-firmware filename.mi4\n");
75 fprintf(stderr," -bl --update-ppbl filename.bin\n");
76 fprintf(stderr,"\n");
77
78#ifdef __WIN32__
79 fprintf(stderr,"DISKNO is the number (e.g. 2) Windows has assigned to your sansa's hard disk.\n");
80 fprintf(stderr,"The first hard disk in your computer (i.e. C:\\) will be disk 0, the next disk\n");
81 fprintf(stderr,"will be disk 1 etc. sansapatcher will refuse to access a disk unless it\n");
82 fprintf(stderr,"can identify it as being an E200 or C200.\n");
83 fprintf(stderr,"\n");
84#else
85#if defined(linux) || defined (__linux)
86 fprintf(stderr,"\"device\" is the device node (e.g. /dev/sda) assigned to your sansa.\n");
87#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
88 fprintf(stderr,"\"device\" is the device node (e.g. /dev/da1) assigned to your sansa.\n");
89#elif defined(__APPLE__) && defined(__MACH__)
90 fprintf(stderr,"\"device\" is the device node (e.g. /dev/disk1) assigned to your sansa.\n");
91#endif
92 fprintf(stderr,"sansapatcher will refuse to access a disk unless it can identify it as being\n");
93 fprintf(stderr,"an E200 or C200.\n");
94#endif
95}
96
97static const char* get_parttype(int pt)
98{
99 int i;
100 static const char unknown[]="Unknown";
101
102 if (pt == -1) {
103 return "HFS/HFS+";
104 }
105
106 i=0;
107 while (parttypes[i].name != NULL) {
108 if (parttypes[i].type == pt) {
109 return (parttypes[i].name);
110 }
111 i++;
112 }
113
114 return unknown;
115}
116
117static void display_partinfo(struct sansa_t* sansa)
118{
119 int i;
120 double sectors_per_MB = (1024.0*1024.0)/sansa->sector_size;
121
122 printf("[INFO] Part Start Sector End Sector Size (MB) Type\n");
123 for ( i = 0; i < 4; i++ ) {
124 if (sansa->pinfo[i].start != 0) {
125 printf("[INFO] %d %10ld %10ld %10.1f %s (0x%02x)\n",
126 i,
127 sansa->pinfo[i].start,
128 sansa->pinfo[i].start+sansa->pinfo[i].size-1,
129 sansa->pinfo[i].size/sectors_per_MB,
130 get_parttype(sansa->pinfo[i].type),
131 sansa->pinfo[i].type);
132 }
133 }
134}
135
136
137int main(int argc, char* argv[])
138{
139 char yesno[4];
140 int i;
141 int n;
142 char* filename;
143 int action = SHOW_INFO;
144 struct sansa_t sansa;
145 int res = 0;
146 unsigned char* buf = NULL;
147 unsigned int len;
148
149 fprintf(stderr,"sansapatcher v" VERSION " - (C) Dave Chapman 2006-2007\n");
150 fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n");
151 fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
152
153 if ((argc > 1) && ((strcmp(argv[1],"-h")==0) || (strcmp(argv[1],"--help")==0))) {
154 print_usage();
155 return SANSA_OK;
156 }
157
158 if (sansa_alloc_buffer(&sansa, BUFFER_SIZE) < 0) {
159 fprintf(stderr,"Failed to allocate memory buffer\n");
160 return SANSA_INTERNAL_ERROR;
161 }
162
163 if ((argc > 1) && (strcmp(argv[1],"--scan")==0)) {
164 if (sansa_scan(&sansa) == 0)
165 fprintf(stderr,"[ERR] No E200s or C200s found.\n");
166 return SANSA_NOT_FOUND;
167 }
168
169 /* If the first parameter doesn't start with -, then we interpret it as a device */
170 if ((argc > 1) && (argv[1][0] != '-')) {
171 sansa.diskname[0]=0;
172#ifdef __WIN32__
173 snprintf(sansa.diskname,sizeof(sansa.diskname),"\\\\.\\PhysicalDrive%s",argv[1]);
174#else
175 strncpy(sansa.diskname,argv[1],sizeof(sansa.diskname));
176#endif
177 i = 2;
178 } else {
179 /* Autoscan for C200/E200s */
180 n = sansa_scan(&sansa);
181 if (n==0) {
182 fprintf(stderr,"[ERR] No E200s or C200s found, aborting\n");
183 fprintf(stderr,"[ERR] Please connect your sansa and ensure it is in UMS mode\n");
184#if defined(__APPLE__) && defined(__MACH__)
185 fprintf(stderr,"[ERR] Also ensure that your Sansa's main partition is not mounted.\n");
186#elif !defined(__WIN32__)
187 if (geteuid()!=0) {
188 fprintf(stderr,"[ERR] You may also need to run sansapatcher as root.\n");
189 }
190#endif
191 fprintf(stderr,"[ERR] Please refer to the Rockbox manual if you continue to have problems.\n");
192 } else if (n > 1) {
193 fprintf(stderr,"[ERR] %d Sansas found, aborting\n",n);
194 fprintf(stderr,"[ERR] Please connect only one Sansa and re-run sansapatcher.\n");
195 }
196
197 if (n != 1) {
198#ifdef WITH_BOOTOBJS
199 if (argc==1) {
200 printf("\nPress ENTER to exit sansapatcher :");
201 fgets(yesno,4,stdin);
202 }
203#endif
204 return n > 1 ? SANSA_MULTIPLE_DEVICES : SANSA_NOT_FOUND;
205 }
206
207 i = 1;
208 }
209
210#ifdef WITH_BOOTOBJS
211 action = INTERACTIVE;
212#endif
213
214 while (i < argc) {
215 if ((strcmp(argv[i],"-l")==0) || (strcmp(argv[i],"--list")==0)) {
216 action = LIST_IMAGES;
217 i++;
218 } else if (strcmp(argv[i],"--install")==0) {
219 action = INSTALL;
220 i++;
221 } else if ((strcmp(argv[i],"-d")==0) ||
222 (strcmp(argv[i],"--delete-bootloader")==0)) {
223 action = DELETE_BOOTLOADER;
224 i++;
225 } else if ((strcmp(argv[i],"-a")==0) ||
226 (strcmp(argv[i],"--add-bootloader")==0)) {
227 action = ADD_BOOTLOADER;
228 i++;
229 if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; }
230 filename=argv[i];
231 i++;
232 } else if ((strcmp(argv[i],"-of")==0) ||
233 (strcmp(argv[i],"--update-original-firmware")==0)) {
234 action = UPDATE_OF;
235 i++;
236 if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; }
237 filename=argv[i];
238 i++;
239 } else if ((strcmp(argv[i],"-bl")==0) ||
240 (strcmp(argv[i],"--update-ppbl")==0)) {
241 action = UPDATE_PPBL;
242 i++;
243 if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; }
244 filename=argv[i];
245 i++;
246 } else if ((strcmp(argv[i],"-rf")==0) ||
247 (strcmp(argv[i],"--read-firmware")==0)) {
248 action = READ_FIRMWARE;
249 i++;
250 if (i == argc) { print_usage(); return SANSA_WRONG_ARGUMENTS; }
251 filename=argv[i];
252 i++;
253 }
254 }
255
256 if (sansa.diskname[0]==0) {
257 print_usage();
258 return SANSA_WRONG_ARGUMENTS;
259 }
260
261 if (sansa_open(&sansa, 0) < 0) {
262 return SANSA_ACCESS_DENIED;
263 }
264
265 fprintf(stderr,"[INFO] Reading partition table from %s\n",sansa.diskname);
266 fprintf(stderr,"[INFO] Sector size is %d bytes\n",sansa.sector_size);
267
268 if (sansa_read_partinfo(&sansa,0) < 0) {
269 return SANSA_PARTITION_ERROR;
270 }
271
272 display_partinfo(&sansa);
273
274 i = is_sansa(&sansa);
275 if (i < 0) {
276 fprintf(stderr,"[ERR] Disk is not an E200 or C200 (%d), aborting.\n",i);
277 return SANSA_WRONG_TYPE;
278 }
279
280 if (sansa.hasoldbootloader) {
281 printf("[ERR] ************************************************************************\n");
282 printf("[ERR] *** OLD ROCKBOX INSTALLATION DETECTED, ABORTING.\n");
283 printf("[ERR] *** You must reinstall the original Sansa firmware before running\n");
284 printf("[ERR] *** sansapatcher for the first time.\n");
285 printf("[ERR] *** See http://www.rockbox.org/wiki/SansaE200Install\n");
286 printf("[ERR] ************************************************************************\n");
287 res = SANSA_OLD_INSTALL;
288 } else {
289 if (action==LIST_IMAGES) {
290 sansa_list_images(&sansa);
291#ifdef WITH_BOOTOBJS
292 } else if (action==INTERACTIVE) {
293
294 printf("Enter i to install the Rockbox bootloader, u to uninstall\n or c to cancel and do nothing (i/u/c) :");
295
296 if (fgets(yesno,4,stdin)) {
297 if (yesno[0]=='i') {
298 if (sansa_reopen_rw(&sansa) < 0) {
299 res = SANSA_CANNOT_REOPEN;
300 }
301 if (strcmp(sansa.targetname,"c200") == 0) {
302 len = LEN_bootimg_c200;
303 buf = bootimg_c200;
304 } else {
305 len = LEN_bootimg_e200;
306 buf = bootimg_e200;
307 }
308 if (sansa_add_bootloader(&sansa, buf, len)==0) {
309 fprintf(stderr,"[INFO] Bootloader installed successfully.\n");
310 } else {
311 fprintf(stderr,"[ERR] --install failed.\n");
312 res = SANSA_INSTALL_FAILED;
313 }
314 } else if (yesno[0]=='u') {
315 if (sansa_reopen_rw(&sansa) < 0) {
316 res = SANSA_CANNOT_REOPEN;
317 }
318
319 if (sansa_delete_bootloader(&sansa)==0) {
320 fprintf(stderr,"[INFO] Bootloader removed.\n");
321 } else {
322 fprintf(stderr,"[ERR] Bootloader removal failed.\n");
323 res = SANSA_UNINSTALL_FAILED;
324 }
325 }
326 }
327#endif
328 } else if (action==READ_FIRMWARE) {
329 if (sansa_read_firmware(&sansa, filename)==0) {
330 fprintf(stderr,"[INFO] Firmware read to file %s.\n",filename);
331 } else {
332 fprintf(stderr,"[ERR] --read-firmware failed.\n");
333 }
334#ifdef WITH_BOOTOBJS
335 } else if (action==INSTALL) {
336 if (sansa_reopen_rw(&sansa) < 0) {
337 return SANSA_CANNOT_REOPEN;
338 }
339
340 if (strcmp(sansa.targetname,"c200") == 0) {
341 len = LEN_bootimg_c200;
342 buf = bootimg_c200;
343 } else {
344 len = LEN_bootimg_e200;
345 buf = bootimg_e200;
346 }
347
348 if (sansa_add_bootloader(&sansa, buf, len)==0) {
349 fprintf(stderr,"[INFO] Bootloader installed successfully.\n");
350 } else {
351 fprintf(stderr,"[ERR] --install failed.\n");
352 }
353#endif
354 } else if (action==ADD_BOOTLOADER) {
355 if (sansa_reopen_rw(&sansa) < 0) {
356 return SANSA_CANNOT_REOPEN;
357 }
358
359 len = sansa_read_bootloader(&sansa, filename, &buf);
360 if (len > 0) {
361 if (sansa_add_bootloader(&sansa, buf, len)==0) {
362 fprintf(stderr,"[INFO] Bootloader %s written to device.\n",filename);
363 } else {
364 fprintf(stderr,"[ERR] --add-bootloader failed.\n");
365 }
366 }
367 } else if (action==DELETE_BOOTLOADER) {
368 if (sansa_reopen_rw(&sansa) < 0) {
369 return SANSA_CANNOT_REOPEN;
370 }
371
372 if (sansa_delete_bootloader(&sansa)==0) {
373 fprintf(stderr,"[INFO] Bootloader removed successfully.\n");
374 } else {
375 fprintf(stderr,"[ERR] --delete-bootloader failed.\n");
376 }
377 } else if (action==UPDATE_OF) {
378 if (sansa_reopen_rw(&sansa) < 0) {
379 return SANSA_CANNOT_REOPEN;
380 }
381
382 if (sansa_update_of(&sansa, filename)==0) {
383 fprintf(stderr,"[INFO] OF updated successfully.\n");
384 } else {
385 fprintf(stderr,"[ERR] --update-original-firmware failed.\n");
386 }
387 } else if (action==UPDATE_PPBL) {
388 printf("[WARN] PPBL installation will overwrite your bootloader. This will lead to a\n");
389 printf(" Sansa that won't boot if the bootloader file is invalid. Only continue if\n");
390 printf(" you're sure you know what you're doing.\n");
391 printf(" Continue (y/n)? ");
392
393 if (fgets(yesno,4,stdin)) {
394 if (yesno[0]=='y') {
395 if (sansa_reopen_rw(&sansa) < 0) {
396 return SANSA_CANNOT_REOPEN;
397 }
398
399 if (sansa_update_ppbl(&sansa, filename)==0) {
400 fprintf(stderr,"[INFO] PPBL updated successfully.\n");
401 } else {
402 fprintf(stderr,"[ERR] --update-ppbl failed.\n");
403 }
404 }
405 }
406 }
407 }
408
409 sansa_close(&sansa);
410 sansa_dealloc_buffer(&sansa);
411
412#ifdef WITH_BOOTOBJS
413 if (action==INTERACTIVE) {
414 printf("Press ENTER to exit sansapatcher :");
415 fgets(yesno,4,stdin);
416 }
417#endif
418
419 return res;
420}
diff --git a/utils/sansapatcher/parttypes.h b/utils/sansapatcher/parttypes.h
new file mode 100644
index 0000000000..f8de303553
--- /dev/null
+++ b/utils/sansapatcher/parttypes.h
@@ -0,0 +1,109 @@
1/* DOS partition types - taken from fdisk */
2
3struct parttype {
4 unsigned char type;
5 char *name;
6};
7
8struct parttype parttypes[] = {
9 {0x00, "Empty"},
10 {0x01, "FAT12"},
11 {0x02, "XENIX root"},
12 {0x03, "XENIX usr"},
13 {0x04, "FAT16 <32M"},
14 {0x05, "Extended"}, /* DOS 3.3+ extended partition */
15 {0x06, "FAT16"}, /* DOS 16-bit >=32M */
16 {0x07, "HPFS/NTFS"}, /* OS/2 IFS, eg, HPFS or NTFS or QNX */
17 {0x08, "AIX"}, /* AIX boot (AIX -- PS/2 port) or SplitDrive */
18 {0x09, "AIX bootable"}, /* AIX data or Coherent */
19 {0x0a, "OS/2 Boot Manager"},/* OS/2 Boot Manager */
20 {0x0b, "W95 FAT32"},
21 {0x0c, "W95 FAT32 (LBA)"},/* LBA really is `Extended Int 13h' */
22 {0x0e, "W95 FAT16 (LBA)"},
23 {0x0f, "W95 Ext'd (LBA)"},
24 {0x10, "OPUS"},
25 {0x11, "Hidden FAT12"},
26 {0x12, "Compaq diagnostics"},
27 {0x14, "Hidden FAT16 <32M"},
28 {0x16, "Hidden FAT16"},
29 {0x17, "Hidden HPFS/NTFS"},
30 {0x18, "AST SmartSleep"},
31 {0x1b, "Hidden W95 FAT32"},
32 {0x1c, "Hidden W95 FAT32 (LBA)"},
33 {0x1e, "Hidden W95 FAT16 (LBA)"},
34 {0x24, "NEC DOS"},
35 {0x39, "Plan 9"},
36 {0x3c, "PartitionMagic recovery"},
37 {0x40, "Venix 80286"},
38 {0x41, "PPC PReP Boot"},
39 {0x42, "SFS"},
40 {0x4d, "QNX4.x"},
41 {0x4e, "QNX4.x 2nd part"},
42 {0x4f, "QNX4.x 3rd part"},
43 {0x50, "OnTrack DM"},
44 {0x51, "OnTrack DM6 Aux1"}, /* (or Novell) */
45 {0x52, "CP/M"}, /* CP/M or Microport SysV/AT */
46 {0x53, "OnTrack DM6 Aux3"},
47 {0x54, "OnTrackDM6"},
48 {0x55, "EZ-Drive"},
49 {0x56, "Golden Bow"},
50 {0x5c, "Priam Edisk"},
51 {0x61, "SpeedStor"},
52 {0x63, "GNU HURD or SysV"}, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
53 {0x64, "Novell Netware 286"},
54 {0x65, "Novell Netware 386"},
55 {0x70, "DiskSecure Multi-Boot"},
56 {0x75, "PC/IX"},
57 {0x80, "Old Minix"}, /* Minix 1.4a and earlier */
58 {0x81, "Minix / old Linux"},/* Minix 1.4b and later */
59 {0x82, "Linux swap / Solaris"},
60 {0x83, "Linux"},
61 {0x84, "OS/2 hidden C: drive"},
62 {0x85, "Linux extended"},
63 {0x86, "NTFS volume set"},
64 {0x87, "NTFS volume set"},
65 {0x88, "Linux plaintext"},
66 {0x8e, "Linux LVM"},
67 {0x93, "Amoeba"},
68 {0x94, "Amoeba BBT"}, /* (bad block table) */
69 {0x9f, "BSD/OS"}, /* BSDI */
70 {0xa0, "IBM Thinkpad hibernation"},
71 {0xa5, "FreeBSD"}, /* various BSD flavours */
72 {0xa6, "OpenBSD"},
73 {0xa7, "NeXTSTEP"},
74 {0xa8, "Darwin UFS"},
75 {0xa9, "NetBSD"},
76 {0xab, "Darwin boot"},
77 {0xb7, "BSDI fs"},
78 {0xb8, "BSDI swap"},
79 {0xbb, "Boot Wizard hidden"},
80 {0xbe, "Solaris boot"},
81 {0xbf, "Solaris"},
82 {0xc1, "DRDOS/sec (FAT-12)"},
83 {0xc4, "DRDOS/sec (FAT-16 < 32M)"},
84 {0xc6, "DRDOS/sec (FAT-16)"},
85 {0xc7, "Syrinx"},
86 {0xda, "Non-FS data"},
87 {0xdb, "CP/M / CTOS / ..."},/* CP/M or Concurrent CP/M or
88 Concurrent DOS or CTOS */
89 {0xde, "Dell Utility"}, /* Dell PowerEdge Server utilities */
90 {0xdf, "BootIt"}, /* BootIt EMBRM */
91 {0xe1, "DOS access"}, /* DOS access or SpeedStor 12-bit FAT
92 extended partition */
93 {0xe3, "DOS R/O"}, /* DOS R/O or SpeedStor */
94 {0xe4, "SpeedStor"}, /* SpeedStor 16-bit FAT extended
95 partition < 1024 cyl. */
96 {0xeb, "BeOS fs"},
97 {0xee, "EFI GPT"}, /* Intel EFI GUID Partition Table */
98 {0xef, "EFI (FAT-12/16/32)"},/* Intel EFI System Partition */
99 {0xf0, "Linux/PA-RISC boot"},/* Linux/PA-RISC boot loader */
100 {0xf1, "SpeedStor"},
101 {0xf4, "SpeedStor"}, /* SpeedStor large partition */
102 {0xf2, "DOS secondary"}, /* DOS 3.3+ secondary */
103 {0xfd, "Linux raid autodetect"},/* New (2.2.x) raid partition with
104 autodetect using persistent
105 superblock */
106 {0xfe, "LANstep"}, /* SpeedStor >1024 cyl. or LANstep */
107 {0xff, "BBT"}, /* Xenix Bad Block Table */
108 { 0, 0 }
109};
diff --git a/utils/sansapatcher/sansaio-posix.c b/utils/sansapatcher/sansaio-posix.c
new file mode 100644
index 0000000000..44c4dcc95c
--- /dev/null
+++ b/utils/sansapatcher/sansaio-posix.c
@@ -0,0 +1,157 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006-2007 Dave Chapman
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#if !defined(_WIN32) /* all non-Windows platforms supported are POSIX. */
23#include <stdio.h>
24#include <unistd.h>
25#include <fcntl.h>
26#include <string.h>
27#include <stdlib.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <sys/ioctl.h>
31#include <errno.h>
32
33#if defined(linux) || defined (__linux)
34#include <sys/mount.h>
35#define SANSA_SECTORSIZE_IOCTL BLKSSZGET
36#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
37 || defined(__bsdi__) || defined(__DragonFly__)
38#include <sys/disk.h>
39#define SANSA_SECTORSIZE_IOCTL DIOCGSECTORSIZE
40#elif defined(__APPLE__) && defined(__MACH__)
41#include <sys/disk.h>
42#define SANSA_SECTORSIZE_IOCTL DKIOCGETBLOCKSIZE
43#else
44 #error No sector-size detection implemented for this platform
45#endif
46
47#include "sansaio.h"
48
49#if defined(__APPLE__) && defined(__MACH__)
50static int sansa_unmount(struct sansa_t* sansa)
51{
52 char cmd[4096];
53 int res;
54
55 sprintf(cmd, "/usr/sbin/diskutil unmount \"%ss1\"",sansa->diskname);
56 fprintf(stderr,"[INFO] ");
57 res = system(cmd);
58
59 if (res==0) {
60 return 0;
61 } else {
62 perror("Unmount failed");
63 return -1;
64 }
65}
66#endif
67
68
69void sansa_print_error(char* msg)
70{
71 perror(msg);
72}
73
74int sansa_open(struct sansa_t* sansa, int silent)
75{
76 sansa->dh=open(sansa->diskname,O_RDONLY);
77 if (sansa->dh < 0) {
78 if (!silent) perror(sansa->diskname);
79 if(errno == EACCES) return -2;
80 else return -1;
81 }
82
83 if(ioctl(sansa->dh,SANSA_SECTORSIZE_IOCTL,&sansa->sector_size) < 0) {
84 sansa->sector_size=512;
85 if (!silent) {
86 fprintf(stderr,"[ERR] ioctl() call to get sector size failed, defaulting to %d\n"
87 ,sansa->sector_size);
88 }
89 }
90 return 0;
91}
92
93
94int sansa_reopen_rw(struct sansa_t* sansa)
95{
96#if defined(__APPLE__) && defined(__MACH__)
97 if (sansa_unmount(sansa) < 0)
98 return -1;
99#endif
100
101 close(sansa->dh);
102 sansa->dh=open(sansa->diskname,O_RDWR);
103 if (sansa->dh < 0) {
104 perror(sansa->diskname);
105 return -1;
106 }
107 return 0;
108}
109
110int sansa_close(struct sansa_t* sansa)
111{
112 close(sansa->dh);
113 return 0;
114}
115
116int sansa_alloc_buffer(struct sansa_t *sansa, int bufsize)
117{
118 sansa->sectorbuf=malloc(bufsize);
119 if (sansa->sectorbuf == NULL) {
120 return -1;
121 }
122 return 0;
123}
124
125int sansa_dealloc_buffer(struct sansa_t* sansa)
126{
127 if (sansa->sectorbuf == NULL) {
128 return -1;
129 }
130 free(sansa->sectorbuf);
131 sansa->sectorbuf = NULL;
132 return 0;
133}
134
135int sansa_seek(struct sansa_t* sansa, loff_t pos)
136{
137 off_t res;
138
139 res = lseek64(sansa->dh, pos, SEEK_SET);
140
141 if (res == -1) {
142 return -1;
143 }
144 return 0;
145}
146
147int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes)
148{
149 return read(sansa->dh, buf, nbytes);
150}
151
152int sansa_write(struct sansa_t* sansa, int nbytes)
153{
154 return write(sansa->dh, sansa->sectorbuf, nbytes);
155}
156#endif
157
diff --git a/utils/sansapatcher/sansaio-win32.c b/utils/sansapatcher/sansaio-win32.c
new file mode 100644
index 0000000000..ee6a8cd93d
--- /dev/null
+++ b/utils/sansapatcher/sansaio-win32.c
@@ -0,0 +1,217 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006-2007 Dave Chapman
11 *
12 * error(), lock_volume() and unlock_volume() functions and inspiration taken
13 * from:
14 * RawDisk - Direct Disk Read/Write Access for NT/2000/XP
15 * Copyright (c) 2003 Jan Kiszka
16 * http://www.stud.uni-hannover.de/user/73174/RawDisk/
17 *
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version 2
21 * of the License, or (at your option) any later version.
22 *
23 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
24 * KIND, either express or implied.
25 *
26 ****************************************************************************/
27
28#if defined(_WIN32)
29#include <stdio.h>
30#include <unistd.h>
31#include <fcntl.h>
32#include <string.h>
33#include <stdlib.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#ifdef __WIN32__
37#include <windows.h>
38#include <winioctl.h>
39#endif
40
41#include "sansaio.h"
42
43static int lock_volume(HANDLE hDisk)
44{
45 DWORD dummy;
46
47 return DeviceIoControl(hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0,
48 &dummy, NULL);
49}
50
51static int unlock_volume(HANDLE hDisk)
52{
53 DWORD dummy;
54
55 return DeviceIoControl(hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0,
56 &dummy, NULL);
57}
58
59void sansa_print_error(char* msg)
60{
61 LPSTR pMsgBuf = NULL;
62
63 printf(msg);
64 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
65 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
66 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), pMsgBuf,
67 0, NULL);
68 printf(pMsgBuf);
69 LocalFree(pMsgBuf);
70}
71
72int sansa_open(struct sansa_t* sansa, int silent)
73{
74 DISK_GEOMETRY_EX diskgeometry_ex;
75 DISK_GEOMETRY diskgeometry;
76 unsigned long n;
77
78 sansa->dh = CreateFileA(sansa->diskname, GENERIC_READ,
79 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
80 FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL);
81
82 if (sansa->dh == INVALID_HANDLE_VALUE) {
83 if (!silent) sansa_print_error(" Error opening disk: ");
84 if(GetLastError() == ERROR_ACCESS_DENIED)
85 return -2;
86 else
87 return -1;
88 }
89
90 if (!lock_volume(sansa->dh)) {
91 if (!silent) sansa_print_error(" Error locking disk: ");
92 return -1;
93 }
94
95 if (!DeviceIoControl(sansa->dh,
96 IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
97 NULL,
98 0,
99 &diskgeometry_ex,
100 sizeof(diskgeometry_ex),
101 &n,
102 NULL)) {
103 if (!DeviceIoControl(sansa->dh,
104 IOCTL_DISK_GET_DRIVE_GEOMETRY,
105 NULL,
106 0,
107 &diskgeometry,
108 sizeof(diskgeometry),
109 &n,
110 NULL)) {
111 if (!silent) sansa_print_error(" Error reading disk geometry: ");
112 return -1;
113 } else {
114 sansa->sector_size=diskgeometry.BytesPerSector;
115 }
116 } else {
117 sansa->sector_size=diskgeometry_ex.Geometry.BytesPerSector;
118 }
119
120 return 0;
121}
122
123int sansa_reopen_rw(struct sansa_t* sansa)
124{
125 /* Close existing file and re-open for writing */
126 unlock_volume(sansa->dh);
127 CloseHandle(sansa->dh);
128
129 sansa->dh = CreateFileA(sansa->diskname, GENERIC_READ | GENERIC_WRITE,
130 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
131 FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL);
132
133 if (sansa->dh == INVALID_HANDLE_VALUE) {
134 sansa_print_error(" Error opening disk: ");
135 return -1;
136 }
137
138 if (!lock_volume(sansa->dh)) {
139 sansa_print_error(" Error locking disk: ");
140 return -1;
141 }
142
143 return 0;
144}
145
146int sansa_close(struct sansa_t* sansa)
147{
148 unlock_volume(sansa->dh);
149 CloseHandle(sansa->dh);
150 return 0;
151}
152
153int sansa_alloc_buffer(struct sansa_t* sansa, int bufsize)
154{
155 /* The ReadFile function requires a memory buffer aligned to a multiple of
156 the disk sector size. */
157 sansa->sectorbuf = (unsigned char*)VirtualAlloc(NULL, bufsize, MEM_COMMIT, PAGE_READWRITE);
158 if (sansa->sectorbuf == NULL) {
159 sansa_print_error(" Error allocating a buffer: ");
160 return -1;
161 }
162 return 0;
163}
164
165int sansa_dealloc_buffer(struct sansa_t* sansa)
166{
167 if (sansa->sectorbuf == NULL) {
168 return -1;
169 }
170 if(!VirtualFree(sansa->sectorbuf, 0, MEM_RELEASE)) {
171 sansa_print_error(" Error releasing buffer ");
172 return -1;
173 }
174 sansa->sectorbuf = NULL;
175 return 0;
176}
177
178int sansa_seek(struct sansa_t* sansa, loff_t pos)
179{
180 LARGE_INTEGER li;
181
182 li.QuadPart = pos;
183
184 li.LowPart = SetFilePointer (sansa->dh, li.LowPart, &li.HighPart, FILE_BEGIN);
185
186 if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
187 sansa_print_error(" Seek error ");
188 return -1;
189 }
190 return 0;
191}
192
193int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes)
194{
195 unsigned long count;
196
197 if (!ReadFile(sansa->dh, buf, nbytes, &count, NULL)) {
198 sansa_print_error(" Error reading from disk: ");
199 return -1;
200 }
201
202 return count;
203}
204
205int sansa_write(struct sansa_t* sansa, int nbytes)
206{
207 unsigned long count;
208
209 if (!WriteFile(sansa->dh, sansa->sectorbuf, nbytes, &count, NULL)) {
210 sansa_print_error(" Error writing to disk: ");
211 return -1;
212 }
213
214 return count;
215}
216#endif
217
diff --git a/utils/sansapatcher/sansaio.h b/utils/sansapatcher/sansaio.h
new file mode 100644
index 0000000000..61e2f1d1b2
--- /dev/null
+++ b/utils/sansapatcher/sansaio.h
@@ -0,0 +1,88 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006-2007 Dave Chapman
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef __SANSAIO_H
23#define __SANSAIO_H
24
25#include <stdint.h>
26#if !defined(_MSC_VER)
27#include <unistd.h> /* not available on MSVC */
28#endif
29
30#if defined(__WIN32__) || defined(_WIN32)
31#include <windows.h>
32#define loff_t int64_t
33#else
34#define HANDLE int
35#define O_BINARY 0
36
37/* Only Linux seems to need lseek64 and loff_t */
38#if !defined(linux) && !defined (__linux)
39#define loff_t off_t
40#define lseek64 lseek
41#endif
42
43#endif
44
45#ifdef __cplusplus
46extern "C" {
47#endif
48
49struct sansa_partinfo_t {
50 unsigned long start; /* first sector (LBA) */
51 unsigned long size; /* number of sectors */
52 int type;
53};
54
55struct mi4header_t {
56 uint32_t version;
57 uint32_t length;
58 uint32_t crc32;
59 uint32_t enctype;
60 uint32_t mi4size;
61 uint32_t plaintext;
62};
63
64struct sansa_t {
65 HANDLE dh;
66 unsigned char* sectorbuf;
67 char diskname[4096];
68 int sector_size;
69 struct sansa_partinfo_t pinfo[4];
70 int hasoldbootloader;
71 char* targetname; /* "e200" or "c200" */
72 loff_t start; /* Offset in bytes of firmware partition from start of disk */
73};
74
75void sansa_print_error(char* msg);
76int sansa_open(struct sansa_t* sansa, int silent);
77int sansa_reopen_rw(struct sansa_t* sansa);
78int sansa_close(struct sansa_t* sansa);
79int sansa_seek(struct sansa_t* sansa, loff_t pos);
80int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes);
81int sansa_write(struct sansa_t* sansa, int nbytes);
82int sansa_alloc_buffer(struct sansa_t* sansa, int bufsize);
83int sansa_dealloc_buffer(struct sansa_t* sansa);
84
85#ifdef __cplusplus
86}
87#endif
88#endif
diff --git a/utils/sansapatcher/sansapatcher.c b/utils/sansapatcher/sansapatcher.c
new file mode 100644
index 0000000000..e3b105dcca
--- /dev/null
+++ b/utils/sansapatcher/sansapatcher.c
@@ -0,0 +1,975 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006-2007 Dave Chapman
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include <stdio.h>
23#include <unistd.h>
24#include <fcntl.h>
25#include <string.h>
26#include <stdlib.h>
27#include <inttypes.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30
31#include "sansaio.h"
32#include "sansapatcher.h"
33
34/* The offset of the MI4 image header in the firmware partition */
35#define PPMI_OFFSET 0x80000
36#define NVPARAMS_OFFSET 0x780000
37#define NVPARAMS_SIZE (0x80000-0x200)
38
39int sansa_verbose = 0;
40
41/* Windows requires the buffer for disk I/O to be aligned in memory on a
42 multiple of the disk volume size - so we use a single global variable
43 and initialise it with sansa_alloc_buf() in main().
44*/
45
46static off_t filesize(int fd) {
47 struct stat buf;
48
49 if (fstat(fd,&buf) < 0) {
50 perror("[ERR] Checking filesize of input file");
51 return -1;
52 } else {
53 return(buf.st_size);
54 }
55}
56
57/* Partition table parsing code taken from Rockbox */
58
59#define MAX_SECTOR_SIZE 2048
60#define SECTOR_SIZE 512
61
62static inline int32_t le2int(const unsigned char* buf)
63{
64 int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
65
66 return res;
67}
68
69static inline uint32_t le2uint(const unsigned char* buf)
70{
71 uint32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
72
73 return res;
74}
75
76static inline void int2le(unsigned int val, unsigned char* addr)
77{
78 addr[0] = val & 0xFF;
79 addr[1] = (val >> 8) & 0xff;
80 addr[2] = (val >> 16) & 0xff;
81 addr[3] = (val >> 24) & 0xff;
82}
83
84#define BYTES2INT32(array,pos)\
85 ((long)array[pos] | ((long)array[pos+1] << 8 ) |\
86 ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
87
88int sansa_read_partinfo(struct sansa_t* sansa, int silent)
89{
90 int i;
91 unsigned long count;
92
93 count = sansa_read(sansa,sansa->sectorbuf, sansa->sector_size);
94
95 if (count <= 0) {
96 sansa_print_error(" Error reading from disk: ");
97 return -1;
98 }
99
100 if ((sansa->sectorbuf[510] == 0x55) && (sansa->sectorbuf[511] == 0xaa)) {
101 /* parse partitions */
102 for ( i = 0; i < 4; i++ ) {
103 unsigned char* ptr = sansa->sectorbuf + 0x1be + 16*i;
104 sansa->pinfo[i].type = ptr[4];
105 sansa->pinfo[i].start = BYTES2INT32(ptr, 8);
106 sansa->pinfo[i].size = BYTES2INT32(ptr, 12);
107
108 /* extended? */
109 if ( sansa->pinfo[i].type == 5 ) {
110 /* not handled yet */
111 }
112 }
113 } else if ((sansa->sectorbuf[0] == 'E') && (sansa->sectorbuf[1] == 'R')) {
114 if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n");
115 return -1;
116 }
117
118 /* Calculate the starting position of the firmware partition */
119 sansa->start = (loff_t)sansa->pinfo[1].start*(loff_t)sansa->sector_size;
120 return 0;
121}
122
123/* NOTE: memmem implementation copied from glibc-2.2.4 - it's a GNU
124 extension and is not universally. In addition, early versions of
125 memmem had a serious bug - the meaning of needle and haystack were
126 reversed. */
127
128/* Copyright (C) 1991,92,93,94,96,97,98,2000 Free Software Foundation, Inc.
129 This file is part of the GNU C Library.
130
131 The GNU C Library is free software; you can redistribute it and/or
132 modify it under the terms of the GNU Lesser General Public
133 License as published by the Free Software Foundation; either
134 version 2.1 of the License, or (at your option) any later version.
135
136 The GNU C Library is distributed in the hope that it will be useful,
137 but WITHOUT ANY WARRANTY; without even the implied warranty of
138 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
139 Lesser General Public License for more details.
140
141 You should have received a copy of the GNU Lesser General Public
142 License along with the GNU C Library; if not, write to the Free
143 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
144 02111-1307 USA. */
145
146/* Return the first occurrence of NEEDLE in HAYSTACK. */
147static void *
148sansa_memmem (haystack, haystack_len, needle, needle_len)
149 const void *haystack;
150 size_t haystack_len;
151 const void *needle;
152 size_t needle_len;
153{
154 const char *begin;
155 const char *const last_possible
156 = (const char *) haystack + haystack_len - needle_len;
157
158 if (needle_len == 0)
159 /* The first occurrence of the empty string is deemed to occur at
160 the beginning of the string. */
161 return (void *) haystack;
162
163 /* Sanity check, otherwise the loop might search through the whole
164 memory. */
165 if (__builtin_expect (haystack_len < needle_len, 0))
166 return NULL;
167
168 for (begin = (const char *) haystack; begin <= last_possible; ++begin)
169 if (begin[0] == ((const char *) needle)[0] &&
170 !memcmp ((const void *) &begin[1],
171 (const void *) ((const char *) needle + 1),
172 needle_len - 1))
173 return (void *) begin;
174
175 return NULL;
176}
177
178/*
179 * CRC32 implementation taken from:
180 *
181 * efone - Distributed internet phone system.
182 *
183 * (c) 1999,2000 Krzysztof Dabrowski
184 * (c) 1999,2000 ElysiuM deeZine
185 *
186 * This program is free software; you can redistribute it and/or
187 * modify it under the terms of the GNU General Public License
188 * as published by the Free Software Foundation; either version
189 * 2 of the License, or (at your option) any later version.
190 *
191 */
192
193/* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab().
194 * so make sure, you call it before using the other
195 * functions!
196 */
197static unsigned int crc_tab[256];
198
199/* chksum_crc() -- to a given block, this one calculates the
200 * crc32-checksum until the length is
201 * reached. the crc32-checksum will be
202 * the result.
203 */
204static unsigned int chksum_crc32 (const unsigned char *block, unsigned int length)
205{
206 register unsigned long crc;
207 unsigned long i;
208
209 crc = 0;
210 for (i = 0; i < length; i++)
211 {
212 crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
213 }
214 return (crc);
215}
216
217/* chksum_crc32gentab() -- to a global crc_tab[256], this one will
218 * calculate the crcTable for crc32-checksums.
219 * it is generated to the polynom [..]
220 */
221
222static void chksum_crc32gentab (void)
223{
224 unsigned long crc, poly;
225 int i, j;
226
227 poly = 0xEDB88320L;
228 for (i = 0; i < 256; i++)
229 {
230 crc = i;
231 for (j = 8; j > 0; j--)
232 {
233 if (crc & 1)
234 {
235 crc = (crc >> 1) ^ poly;
236 }
237 else
238 {
239 crc >>= 1;
240 }
241 }
242 crc_tab[i] = crc;
243 }
244}
245
246/* Known keys for Sansa E200 and C200 firmwares: */
247#define NUM_KEYS ((int)(sizeof(keys)/sizeof(keys[0])))
248static const uint32_t keys[][4] = {
249 { 0xe494e96e, 0x3ee32966, 0x6f48512b, 0xa93fbb42 }, /* "sansa" */
250 { 0xd7b10538, 0xc662945b, 0x1b3fce68, 0xf389c0e6 }, /* "sansa_gh" */
251 { 0x1d29ddc0, 0x2579c2cd, 0xce339e1a, 0x75465dfe }, /* sansa 103 */
252
253 { 0x2a7968de, 0x15127979, 0x142e60a7, 0xe49c1893 }, /* c200 1.00.03 */
254 { 0xbf2d06fa, 0xf0e23d59, 0x29738132, 0xe2d04ca7 }, /* c200 1.00.04 and up*/
255 { 0xa913d139, 0xf842f398, 0x3e03f1a6, 0x060ee012 }, /* c200 1.01.05 and up*/
256 { 0x0fe92902, 0xe8cc0f89, 0x6ff568ba, 0x1eff5161 }, /* c200 1.01.07 */
257};
258
259/*
260
261tea_decrypt() from http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
262
263"Following is an adaptation of the reference encryption and decryption
264routines in C, released into the public domain by David Wheeler and
265Roger Needham:"
266
267*/
268
269/* NOTE: The mi4 version of TEA uses a different initial value to sum compared
270 to the reference implementation and the main loop is 8 iterations, not
271 32.
272*/
273
274static void tea_decrypt(uint32_t* v0, uint32_t* v1, const uint32_t* k) {
275 uint32_t sum=0xF1BBCDC8, i; /* set up */
276 uint32_t delta=0x9E3779B9; /* a key schedule constant */
277 uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
278 for(i=0; i<8; i++) { /* basic cycle start */
279 *v1 -= ((*v0<<4) + k2) ^ (*v0 + sum) ^ ((*v0>>5) + k3);
280 *v0 -= ((*v1<<4) + k0) ^ (*v1 + sum) ^ ((*v1>>5) + k1);
281 sum -= delta; /* end cycle */
282 }
283}
284
285/* mi4 files are encrypted in 64-bit blocks (two little-endian 32-bit
286 integers) and the key is incremented after each block
287 */
288
289static void tea_decrypt_buf(const unsigned char* src, unsigned char* dest,
290 size_t n, const uint32_t * initial_key)
291{
292 uint32_t v0, v1;
293 unsigned int i;
294 uint32_t key[4];
295
296 memcpy(key, initial_key, sizeof(key));
297 for (i = 0; i < (n / 8); i++) {
298 v0 = le2int(src);
299 v1 = le2int(src+4);
300
301 tea_decrypt(&v0, &v1, key);
302
303 int2le(v0, dest);
304 int2le(v1, dest+4);
305
306 src += 8;
307 dest += 8;
308
309 /* Now increment the key */
310 key[0]++;
311 if (key[0]==0) {
312 key[1]++;
313 if (key[1]==0) {
314 key[2]++;
315 if (key[2]==0) {
316 key[3]++;
317 }
318 }
319 }
320 }
321}
322
323static int get_mi4header(const unsigned char* buf,struct mi4header_t* mi4header)
324{
325 if (memcmp(buf,"PPOS",4)!=0)
326 return -1;
327
328 mi4header->version = le2int(buf+0x04);
329 mi4header->length = le2int(buf+0x08);
330 mi4header->crc32 = le2int(buf+0x0c);
331 mi4header->enctype = le2int(buf+0x10);
332 mi4header->mi4size = le2int(buf+0x14);
333 mi4header->plaintext = le2int(buf+0x18);
334
335 return 0;
336}
337
338static int set_mi4header(unsigned char* buf,const struct mi4header_t* mi4header)
339{
340 if (memcmp(buf,"PPOS",4)!=0)
341 return -1;
342
343 int2le(mi4header->version ,buf+0x04);
344 int2le(mi4header->length ,buf+0x08);
345 int2le(mi4header->crc32 ,buf+0x0c);
346 int2le(mi4header->enctype ,buf+0x10);
347 int2le(mi4header->mi4size ,buf+0x14);
348 int2le(mi4header->plaintext ,buf+0x18);
349
350 /* Add a dummy DSA signature */
351 memset(buf+0x1c,0,40);
352 buf[0x2f] = 1;
353
354 return 0;
355}
356
357static int sansa_seek_and_read(struct sansa_t* sansa, loff_t pos, unsigned char* buf, int nbytes)
358{
359 int n;
360
361 if (sansa_seek(sansa, pos) < 0) {
362 return -1;
363 }
364
365 if ((n = sansa_read(sansa,buf,nbytes)) < 0) {
366 return -1;
367 }
368
369 if (n < nbytes) {
370 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
371 nbytes,n);
372 return -1;
373 }
374
375 return 0;
376}
377
378
379/* We identify an E200 based on the following criteria:
380
381 1) Exactly two partitions;
382 2) First partition is type "W95 FAT32" (0x0b or 0x0c);
383 3) Second partition is type "OS/2 hidden C: drive" (0x84);
384 4) The "PPBL" string appears at offset 0 in the 2nd partition;
385 5) The "PPMI" string appears at offset PPMI_OFFSET in the 2nd partition.
386*/
387
388int is_sansa(struct sansa_t* sansa)
389{
390 struct mi4header_t mi4header;
391 int ppmi_length;
392 int ppbl_length;
393
394 /* Check partition layout */
395 if (((sansa->pinfo[0].type != 0x06) &&
396 (sansa->pinfo[0].type != 0x0b) &&
397 (sansa->pinfo[0].type != 0x0c) &&
398 (sansa->pinfo[0].type != 0x0e)) ||
399 (sansa->pinfo[1].type != 0x84) ||
400 (sansa->pinfo[2].type != 0x00) ||
401 (sansa->pinfo[3].type != 0x00)) {
402 /* Bad partition layout, abort */
403 return -1;
404 }
405
406 /* Check Bootloader header */
407 if (sansa_seek_and_read(sansa, sansa->start, sansa->sectorbuf, 0x200) < 0) {
408 return -2;
409 }
410 if (memcmp(sansa->sectorbuf,"PPBL",4)!=0) {
411 /* No bootloader header, abort */
412 return -4;
413 }
414 ppbl_length = (le2int(sansa->sectorbuf+4) + 0x1ff) & ~0x1ff;
415
416 /* Sanity/safety check - the bootloader can't be larger than PPMI_OFFSET */
417 if (ppbl_length > PPMI_OFFSET)
418 {
419 return -5;
420 }
421
422 /* Load Sansa bootloader and check for "Sansa C200" magic string */
423 if (sansa_seek_and_read(sansa, sansa->start + 0x200, sansa->sectorbuf, ppbl_length) < 0) {
424 fprintf(stderr,"[ERR] Seek and read to 0x%08"PRIx64" in is_sansa failed.\n",
425 sansa->start+0x200);
426 return -6;
427 }
428 if (sansa_memmem(sansa->sectorbuf, ppbl_length, "Sansa C200", 10) != NULL) {
429 /* C200 */
430 sansa->targetname="c200";
431 } else {
432 /* E200 */
433 sansa->targetname="e200";
434 }
435
436 /* Check Main firmware header */
437 if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sansa->sectorbuf, 0x200) < 0) {
438 fprintf(stderr,"[ERR] Seek to 0x%"PRIx64" in is_sansa failed.\n",
439 sansa->start+PPMI_OFFSET);
440 return -5;
441 }
442 if (memcmp(sansa->sectorbuf,"PPMI",4)!=0) {
443 /* No bootloader header, abort */
444 return -7;
445 }
446 ppmi_length = le2int(sansa->sectorbuf+4);
447
448 /* Check main mi4 file header */
449 if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET+0x200, sansa->sectorbuf, 0x200) < 0) {
450 fprintf(stderr,"[ERR] Seek to 0x%"PRIx64" in is_sansa failed.\n",
451 sansa->start+PPMI_OFFSET+0x200);
452 return -5;
453 }
454
455 if (get_mi4header(sansa->sectorbuf,&mi4header) < 0) {
456 fprintf(stderr,"[ERR] Invalid mi4header\n");
457 return -6;
458 }
459
460 /* Some sanity checks:
461
462 1) Main MI4 image without RBBL and < 100000 bytes -> old install
463 2) Main MI4 image with RBBL but no second image -> old install
464 */
465
466 sansa->hasoldbootloader = 0;
467 if (memcmp(sansa->sectorbuf+0x1f8,"RBBL",4)==0) {
468 /* Look for an original firmware after the first image */
469 if (sansa_seek_and_read(sansa,
470 sansa->start + PPMI_OFFSET + 0x200 + ppmi_length,
471 sansa->sectorbuf, 512) < 0) {
472 return -7;
473 }
474
475 if (get_mi4header(sansa->sectorbuf,&mi4header)!=0) {
476 fprintf(stderr,"[ERR] No original firmware found\n");
477 sansa->hasoldbootloader = 1;
478 }
479 } else if (mi4header.mi4size < 100000) {
480 fprintf(stderr,"[ERR] Old bootloader found\n");
481 sansa->hasoldbootloader = 1;
482 }
483
484 return 0;
485}
486
487int sansa_scan(struct sansa_t* sansa)
488{
489 int i;
490 int n = 0;
491 char last_disk[4096];
492 int denied = 0;
493 int result;
494
495 printf("[INFO] Scanning disk devices...\n");
496
497 for (i = 0; i <= 25 ; i++) {
498#ifdef __WIN32__
499 sprintf(sansa->diskname,"\\\\.\\PhysicalDrive%d",i);
500#elif defined(linux) || defined (__linux)
501 sprintf(sansa->diskname,"/dev/sd%c",'a'+i);
502#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
503 || defined(__bsdi__) || defined(__DragonFly__)
504 sprintf(sansa->diskname,"/dev/da%d",i);
505#elif defined(__APPLE__) && defined(__MACH__)
506 sprintf(sansa->diskname,"/dev/disk%d",i);
507#else
508#error No disk paths defined for this platform
509#endif
510 if ((result = sansa_open(sansa, 1)) < 0) {
511 if(result == -2) {
512 denied++;
513 }
514 sansa_close(sansa);
515 continue;
516 }
517
518 if (sansa_read_partinfo(sansa,1) < 0) {
519 sansa_close(sansa);
520 continue;
521 }
522
523 if (is_sansa(sansa) < 0) {
524 sansa_close(sansa);
525 continue;
526 }
527
528#ifdef __WIN32__
529 printf("[INFO] %s found - disk device %d\n",sansa->targetname, i);
530#else
531 printf("[INFO] %s found - %s\n",sansa->targetname, sansa->diskname);
532#endif
533 n++;
534 strcpy(last_disk,sansa->diskname);
535 sansa_close(sansa);
536 }
537
538 if (n==1) {
539 /* Remember the disk name */
540 strcpy(sansa->diskname,last_disk);
541 }
542 else if (n == 0 && denied) {
543 printf("[ERR] FATAL: Permission denied on %d device(s) and no sansa detected.\n", denied);
544#ifdef __WIN32__
545 printf("[ERR] You need to run this program with administrator priviledges!\n");
546#else
547 printf("[ERR] You need permissions for raw disc access for this program to work!\n");
548#endif
549 }
550
551 return (n == 0 && denied) ? -1 : n;
552}
553
554/* Prepare original firmware for writing to the firmware partition by decrypting
555 and updating the header */
556static int prepare_original_firmware(struct sansa_t* sansa, unsigned char* buf, struct mi4header_t* mi4header)
557{
558 unsigned char* tmpbuf;
559 int i;
560 int key_found;
561
562 get_mi4header(buf,mi4header);
563
564#if 0
565 printf("mi4header->version =0x%08x\n",mi4header->version);
566 printf("mi4header->length =0x%08x\n",mi4header->length);
567 printf("mi4header->crc32 =0x%08x\n",mi4header->crc32);
568 printf("mi4header->enctype =0x%08x\n",mi4header->enctype);
569 printf("mi4header->mi4size =0x%08x\n",mi4header->mi4size);
570 printf("mi4header->plaintext =0x%08x\n",mi4header->plaintext);
571#endif
572
573 /* Decrypt anything that needs decrypting. */
574 if (mi4header->plaintext < mi4header->mi4size - 0x200) {
575 /* TODO: Check different keys */
576 tmpbuf=malloc(mi4header->mi4size-(mi4header->plaintext+0x200));
577 if (tmpbuf==NULL) {
578 fprintf(stderr,"[ERR] Can not allocate memory\n");
579 return -1;
580 }
581
582 key_found=0;
583 for (i=0; i < NUM_KEYS && !key_found ; i++) {
584 tea_decrypt_buf(buf+(mi4header->plaintext+0x200),
585 tmpbuf,
586 mi4header->mi4size-(mi4header->plaintext+0x200),
587 keys[i]);
588 key_found = (le2uint(tmpbuf+mi4header->length-mi4header->plaintext-4) == 0xaa55aa55);
589 }
590
591 if (key_found) {
592 memcpy(buf+(mi4header->plaintext+0x200),tmpbuf,mi4header->mi4size-(mi4header->plaintext+0x200));
593 free(tmpbuf);
594 } else {
595 fprintf(stderr,"[ERR] Failed to decrypt image, aborting\n");
596 free(tmpbuf);
597 return -1;
598 }
599 }
600
601 /* Increase plaintext value to full file */
602 mi4header->plaintext = mi4header->mi4size - 0x200;
603
604 /* Update CRC checksum */
605 chksum_crc32gentab ();
606 mi4header->crc32 = chksum_crc32(buf+0x200,mi4header->mi4size-0x200);
607
608 set_mi4header(buf,mi4header);
609
610 /* Add Rockbox-specific header */
611 memcpy(buf+0x1f8,"RBOF",4);
612 memcpy(buf+0x1fc,sansa->targetname,4);
613
614 return 0;
615}
616
617static int load_original_firmware(struct sansa_t* sansa, unsigned char* buf, struct mi4header_t* mi4header)
618{
619 int ppmi_length;
620 int n;
621
622 /* Read 512 bytes from PPMI_OFFSET - the PPMI header plus the mi4 header */
623 if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET, buf, 512) < 0) {
624 return -1;
625 }
626
627 /* No need to check PPMI magic - it's done during init to confirm
628 this is an E200 */
629 ppmi_length = le2int(buf+4);
630
631 /* Firstly look for an original firmware after the first image */
632 if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, buf, 512) < 0) {
633 return -1;
634 }
635
636 if (get_mi4header(buf,mi4header)==0) {
637 /* We have a valid MI4 file after a bootloader, so we use this. */
638 if ((n = sansa_seek_and_read(sansa,
639 sansa->start + PPMI_OFFSET + 0x200 + ppmi_length,
640 buf, mi4header->mi4size)) < 0) {
641 return -1;
642 }
643 } else {
644 /* No valid MI4 file, so read the first image. */
645 if ((n = sansa_seek_and_read(sansa,
646 sansa->start + PPMI_OFFSET + 0x200,
647 buf, ppmi_length)) < 0) {
648 return -1;
649 }
650 }
651 return prepare_original_firmware(sansa, buf, mi4header);
652}
653
654int sansa_read_firmware(struct sansa_t* sansa, const char* filename)
655{
656 int res;
657 int outfile;
658 struct mi4header_t mi4header;
659
660 res = load_original_firmware(sansa,sansa->sectorbuf,&mi4header);
661 if (res < 0)
662 return res;
663
664 outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
665 if (outfile < 0) {
666 fprintf(stderr,"[ERR] Couldn't open file %s\n",filename);
667 return -1;
668 }
669
670 res = write(outfile,sansa->sectorbuf,mi4header.mi4size);
671 if (res != (int)mi4header.mi4size) {
672 fprintf(stderr,"[ERR] Write error - %d\n", res);
673 return -1;
674 }
675 close(outfile);
676
677 return 0;
678}
679
680unsigned int sansa_read_bootloader(struct sansa_t* sansa, const char* filename, unsigned char** bl_buffer)
681{
682 /* Step 1 - read bootloader into RAM. */
683 int infile;
684 unsigned int n;
685 unsigned int len;
686 infile=open(filename,O_RDONLY|O_BINARY);
687 if (infile < 0) {
688 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
689 return 0;
690 }
691
692 len = filesize(infile);
693
694 unsigned char* b = malloc(len);
695 if (b == NULL) {
696 fprintf(stderr,"[ERR] Could not allocate memory for bootloader\n");
697 close(infile);
698 return 0;
699 }
700
701 n = read(infile,b,len);
702 close(infile);
703 if (n < len) {
704 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n"
705 ,len,n);
706 return 0;
707 }
708
709 if (memcmp(b+0x1f8,"RBBL",4)!=0) {
710 fprintf(stderr,"[ERR] %s is not a Rockbox bootloader, aborting.\n",
711 filename);
712 return 0;
713 }
714 if (memcmp(b+0x1fc,sansa->targetname,4)!=0) {
715 fprintf(stderr,"[ERR] %s is not a Rockbox bootloader for %s, aborting.\n",
716 filename, sansa->targetname);
717 return 0;
718 }
719 *bl_buffer = b;
720 return len;
721}
722
723int sansa_add_bootloader(struct sansa_t* sansa, const unsigned char* bootloader, const unsigned int bl_length)
724{
725 int res;
726 struct mi4header_t mi4header;
727 int length;
728 int n;
729
730 /* Create PPMI header */
731 memset(sansa->sectorbuf,0,0x200);
732 memcpy(sansa->sectorbuf,"PPMI",4);
733 int2le(bl_length, sansa->sectorbuf+4);
734 int2le(0x00020000, sansa->sectorbuf+8);
735
736 /* copy bootloader to sansa->sectorbuf+0x200 */
737 memcpy(sansa->sectorbuf+0x200,bootloader,bl_length);
738
739 /* Load original firmware from Sansa to the space after the bootloader */
740 res = load_original_firmware(sansa,sansa->sectorbuf+0x200+bl_length,&mi4header);
741 if (res < 0)
742 return res;
743
744 /* Now write the whole thing back to the Sansa */
745
746 if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) {
747 fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in add_bootloader failed.\n",
748 sansa->start+PPMI_OFFSET);
749 return -5;
750 }
751
752 length = 0x200 + bl_length + mi4header.mi4size;
753
754 n=sansa_write(sansa, length);
755 if (n < length) {
756 fprintf(stderr,"[ERR] Short write in add_bootloader\n");
757 return -6;
758 }
759
760 return 0;
761}
762
763int sansa_delete_bootloader(struct sansa_t* sansa)
764{
765 int res;
766 struct mi4header_t mi4header;
767 int n;
768 int length;
769
770 /* Load original firmware from Sansa to sansa->sectorbuf+0x200 */
771 res = load_original_firmware(sansa,sansa->sectorbuf+0x200,&mi4header);
772 if (res < 0)
773 return res;
774
775 /* Create PPMI header */
776 memset(sansa->sectorbuf,0,0x200);
777 memcpy(sansa->sectorbuf,"PPMI",4);
778 int2le(mi4header.mi4size, sansa->sectorbuf+4);
779 int2le(0x00020000, sansa->sectorbuf+8);
780
781 /* Now write the whole thing back to the Sansa */
782
783 if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) {
784 fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in add_bootloader failed.\n",
785 sansa->start+PPMI_OFFSET);
786 return -5;
787 }
788
789 length = 0x200 + mi4header.mi4size;
790
791 n=sansa_write(sansa, length);
792 if (n < length) {
793 fprintf(stderr,"[ERR] Short write in delete_bootloader\n");
794 return -6;
795 }
796
797 return 0;
798}
799
800/** List number of MI4 images on the player, return number.
801 */
802int sansa_list_images(struct sansa_t* sansa)
803{
804 struct mi4header_t mi4header;
805 loff_t ppmi_length;
806 int num = 0;
807
808 /* Check Main firmware header */
809 if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sansa->sectorbuf, 0x200) < 0) {
810 return 0;
811 }
812
813 ppmi_length = le2int(sansa->sectorbuf+4);
814
815 printf("[INFO] Image 1 - %"PRIu64" bytes\n",ppmi_length);
816 num = 1;
817
818 /* Look for an original firmware after the first image */
819 if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, sansa->sectorbuf, 512) < 0) {
820 return 0;
821 }
822
823 if (get_mi4header(sansa->sectorbuf,&mi4header)==0) {
824 printf("[INFO] Image 2 - %d bytes\n",mi4header.mi4size);
825 num = 2;
826 }
827 return num;
828}
829
830int sansa_update_of(struct sansa_t* sansa, const char* filename)
831{
832 int n;
833 int infile = -1; /* Prevent an erroneous "may be used uninitialised" gcc warning */
834 int of_length = 0; /* Keep gcc happy when building for rbutil */
835 int ppmi_length;
836 struct mi4header_t mi4header;
837 unsigned char buf[512];
838
839 /* Step 1 - check we have an OF on the Sansa to upgrade. We expect the
840 Rockbox bootloader to be installed and the OF to be after it on disk. */
841
842 /* Read 512 bytes from PPMI_OFFSET - the PPMI header */
843 if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET,
844 buf, 512) < 0) {
845 return -1;
846 }
847
848 /* No need to check PPMI magic - it's done during init to confirm
849 this is an E200 */
850 ppmi_length = le2int(buf+4);
851
852 /* Look for an original firmware after the first image */
853 if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET+0x200+ppmi_length,
854 buf, 512) < 0) {
855 return -1;
856 }
857
858 if (get_mi4header(buf,&mi4header)!=0) {
859 /* We don't have a valid MI4 file after a bootloader, so do nothing. */
860 fprintf(stderr,"[ERR] No original firmware found at 0x%08"PRIx64"\n",
861 sansa->start+PPMI_OFFSET+0x200+ppmi_length);
862 return -1;
863 }
864
865 /* Step 2 - read OF into RAM. */
866 infile=open(filename,O_RDONLY|O_BINARY);
867 if (infile < 0) {
868 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
869 return -1;
870 }
871
872 of_length = filesize(infile);
873
874 /* Load original firmware from file */
875 memset(sansa->sectorbuf,0,0x200);
876 n = read(infile,sansa->sectorbuf,of_length);
877 close(infile);
878 if (n < of_length) {
879 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n"
880 , of_length, n);
881 return -1;
882 }
883
884 /* Check we have a valid MI4 file. */
885 if (get_mi4header(sansa->sectorbuf,&mi4header)!=0) {
886 fprintf(stderr,"[ERR] %s is not a valid mi4 file\n",filename);
887 return -1;
888 }
889
890 /* Decrypt and build the header */
891 if(prepare_original_firmware(sansa, sansa->sectorbuf, &mi4header)!=0){
892 fprintf(stderr,"[ERR] Unable to build decrypted mi4 from %s\n"
893 ,filename);
894 return -1;
895 }
896
897 /* Step 3 - write the OF to the Sansa */
898 if (sansa_seek(sansa, sansa->start+PPMI_OFFSET+0x200+ppmi_length) < 0) {
899 fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_of failed.\n",
900 sansa->start+PPMI_OFFSET+0x200+ppmi_length);
901 return -1;
902 }
903
904 n=sansa_write(sansa, of_length);
905 if (n < of_length) {
906 fprintf(stderr,"[ERR] Short write in sansa_update_of\n");
907 return -1;
908 }
909
910 /* Step 4 - zero out the nvparams section - we have to do this or we end up
911 with multiple copies of the nvparams data and don't know which one to
912 work with for the database rebuild disabling trick in our bootloader */
913 if (strcmp(sansa->targetname,"e200") == 0) {
914 printf("[INFO] Resetting Original Firmware settings\n");
915 if (sansa_seek(sansa, sansa->start+NVPARAMS_OFFSET+0x200) < 0) {
916 fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_of failed.\n",
917 sansa->start+NVPARAMS_OFFSET+0x200);
918 return -1;
919 }
920
921 memset(sansa->sectorbuf,0,NVPARAMS_SIZE);
922 n=sansa_write(sansa, NVPARAMS_SIZE);
923 if (n < NVPARAMS_SIZE) {
924 fprintf(stderr,"[ERR] Short write in sansa_update_of\n");
925 return -1;
926 }
927 }
928
929 return 0;
930}
931
932/* Update the PPBL (bootloader) image in the hidden firmware partition */
933int sansa_update_ppbl(struct sansa_t* sansa, const char* filename)
934{
935 int n;
936 int infile = -1; /* Prevent an erroneous "may be used uninitialised" gcc warning */
937 int ppbl_length = 0; /* Keep gcc happy when building for rbutil */
938
939 /* Step 1 - read bootloader into RAM. */
940 infile=open(filename,O_RDONLY|O_BINARY);
941 if (infile < 0) {
942 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
943 return -1;
944 }
945
946 ppbl_length = filesize(infile);
947
948 n = read(infile,sansa->sectorbuf+0x200,ppbl_length);
949 close(infile);
950 if (n < ppbl_length) {
951 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", ppbl_length, n);
952 return -1;
953 }
954
955 /* Step 2 - Build the header */
956 memset(sansa->sectorbuf,0,0x200);
957 memcpy(sansa->sectorbuf,"PPBL",4);
958 int2le(ppbl_length, sansa->sectorbuf+4);
959 int2le(0x00010000, sansa->sectorbuf+8);
960
961 /* Step 3 - write the bootloader to the Sansa */
962 if (sansa_seek(sansa, sansa->start) < 0) {
963 fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_ppbl failed.\n", sansa->start);
964 return -1;
965 }
966
967 n=sansa_write(sansa, ppbl_length + 0x200);
968 if (n < (ppbl_length+0x200)) {
969 fprintf(stderr,"[ERR] Short write in sansa_update_ppbl\n");
970 return -1;
971 }
972
973 return 0;
974}
975
diff --git a/utils/sansapatcher/sansapatcher.h b/utils/sansapatcher/sansapatcher.h
new file mode 100644
index 0000000000..259143e38a
--- /dev/null
+++ b/utils/sansapatcher/sansapatcher.h
@@ -0,0 +1,67 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006-2007 Dave Chapman
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef _SANSAPATCHER_H
23#define _SANSAPATCHER_H
24
25#ifdef __cplusplus
26extern "C" {
27#endif
28
29#include "sansaio.h"
30
31/* exit codes */
32#define SANSA_OK 0
33#define SANSA_WRONG_ARGUMENTS 1
34#define SANSA_OPEN_INFILE_FAILED 2
35#define SANSA_PARTITION_ERROR 3
36#define SANSA_CANNOT_REOPEN 5
37#define SANSA_INSTALL_FAILED 6
38#define SANSA_UNINSTALL_FAILED 7
39#define SANSA_ACCESS_DENIED 10
40#define SANSA_NOT_FOUND 11
41#define SANSA_WRONG_DEVICE_COUNT 12
42#define SANSA_MULTIPLE_DEVICES 15
43#define SANSA_WRONG_TYPE 16
44#define SANSA_OLD_INSTALL 17
45#define SANSA_INTERNAL_ERROR 20
46
47extern int sansa_verbose;
48/* Size of buffer for disk I/O - 8MB is large enough for any version
49 of the Apple firmware, but not the Nano's RSRC image. */
50#define BUFFER_SIZE 8*1024*1024
51
52int sansa_read_partinfo(struct sansa_t* sansa, int silent);
53int is_sansa(struct sansa_t* sansa);
54int sansa_scan(struct sansa_t* sansa);
55int sansa_read_firmware(struct sansa_t* sansa, const char* filename);
56unsigned int sansa_read_bootloader(struct sansa_t* sansa, const char* filename, unsigned char** bl_buffer);
57int sansa_add_bootloader(struct sansa_t* sansa, const unsigned char* buf, unsigned int len);
58int sansa_delete_bootloader(struct sansa_t* sansa);
59int sansa_update_of(struct sansa_t* sansa,const char* filename);
60int sansa_update_ppbl(struct sansa_t* sansa,const char* filename);
61int sansa_list_images(struct sansa_t* sansa);
62
63#ifdef __cplusplus
64}
65#endif
66#endif
67
diff --git a/utils/sansapatcher/sansapatcher.manifest b/utils/sansapatcher/sansapatcher.manifest
new file mode 100644
index 0000000000..71bb153688
--- /dev/null
+++ b/utils/sansapatcher/sansapatcher.manifest
@@ -0,0 +1,13 @@
1<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
3 <assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="sansapatcher.exe" type="win32"/>
4
5 <!-- Identify the application security requirements. -->
6 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
7 <security>
8 <requestedPrivileges>
9 <requestedExecutionLevel level="requireAdministrator"/>
10 </requestedPrivileges>
11 </security>
12 </trustInfo>
13</assembly>
diff --git a/utils/sansapatcher/sansapatcher.pro b/utils/sansapatcher/sansapatcher.pro
new file mode 100644
index 0000000000..f8308e6283
--- /dev/null
+++ b/utils/sansapatcher/sansapatcher.pro
@@ -0,0 +1,38 @@
1#
2# __________ __ ___.
3# Open \______ \ ____ ____ | | _\_ |__ _______ ___
4# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7# \/ \/ \/ \/ \/
8#
9# All files in this archive are subject to the GNU General Public License.
10# See the file COPYING in the source tree root for full license agreement.
11#
12# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
13# KIND, either express or implied.
14#
15
16TEMPLATE = app
17TARGET = sansapatcher
18QT -= core
19
20SOURCES += \
21 sansaio-posix.c \
22 sansaio-win32.c \
23 sansapatcher.c \
24 main.c
25
26HEADERS += \
27 parttypes.h \
28 sansaio.h \
29 sansapatcher.h \
30
31RC_FILE = sansapatcher.rc
32
33DEFINES += _LARGEFILE64_SOURCE
34
35unix {
36 target.path = /usr/local/bin
37 INSTALLS += target
38}
diff --git a/utils/sansapatcher/sansapatcher.rc b/utils/sansapatcher/sansapatcher.rc
new file mode 100644
index 0000000000..26bb8fe730
--- /dev/null
+++ b/utils/sansapatcher/sansapatcher.rc
@@ -0,0 +1 @@
1 24 MOVEABLE PURE "sansapatcher.manifest"