summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Chapman <dave@dchapman.com>2008-05-11 18:29:53 +0000
committerDave Chapman <dave@dchapman.com>2008-05-11 18:29:53 +0000
commit1aa6cde3eaa49c6607cc5827c0a7c8e6dd355a1a (patch)
treee5917680a7ea7c4abf18edf2a7251f894b4c63aa
parentd24eb9e461ca1d25a83d825d355710ebdbe509b5 (diff)
downloadrockbox-1aa6cde3eaa49c6607cc5827c0a7c8e6dd355a1a.tar.gz
rockbox-1aa6cde3eaa49c6607cc5827c0a7c8e6dd355a1a.zip
Some tools for hacking Sansa V2 (AMS) firmware files and injecting our own code.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17464 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--utils/AMS/hacking/Makefile34
-rw-r--r--utils/AMS/hacking/README16
-rw-r--r--utils/AMS/hacking/amsinfo.c175
-rw-r--r--utils/AMS/hacking/mkamsboot.c289
-rw-r--r--utils/AMS/hacking/test.S11
5 files changed, 525 insertions, 0 deletions
diff --git a/utils/AMS/hacking/Makefile b/utils/AMS/hacking/Makefile
new file mode 100644
index 0000000000..7a10c20ae4
--- /dev/null
+++ b/utils/AMS/hacking/Makefile
@@ -0,0 +1,34 @@
1
2# Change INFILE to point to your original firmware file
3INFILE=$(HOME)/FW/AMS/CLIP/m300a-1.1.17A.bin
4
5# OUTFILE is the file you copy to your device's root and rename to
6# (e.g.) m300a.bin
7OUTFILE=patched.bin
8
9
10all: amsinfo $(OUTFILE)
11
12amsinfo: amsinfo.c
13 gcc -o amsinfo -W -Wall amsinfo.c
14
15mkamsboot: mkamsboot.c
16 gcc -o mkamsboot -W -Wall mkamsboot.c
17
18# Rules for our test ARM application - assemble, link, then extract
19# the binary code
20
21test.o: test.S
22 arm-elf-as -o test.o test.S
23
24test.elf: test.o
25 arm-elf-ld -e 0 -o test.elf test.o
26
27test.bin: test.elf
28 arm-elf-objcopy -O binary test.elf test.bin
29
30$(OUTFILE): mkamsboot test.bin $(INFILE)
31 ./mkamsboot $(INFILE) test.bin $(OUTFILE)
32
33clean:
34 rm -fr amsinfo mkamsboot test.bin test.o test.elf $(OUTFILE) *~
diff --git a/utils/AMS/hacking/README b/utils/AMS/hacking/README
new file mode 100644
index 0000000000..ce16f954cd
--- /dev/null
+++ b/utils/AMS/hacking/README
@@ -0,0 +1,16 @@
1This directory contains the following tools related to the Sansa V2
2(AMS) firmware files:
3
41) amsinfo
5
6A tool that dumps information from an AMS firmware file.
7
82) mkamsboot
9
10A tool to inject some code (contained in test.S) into a firmware file.
11
12Edit the INFILE variable in the Makefile to point to the original
13firmware file you want to patch, edit "test.S" appropriately, and then
14type "make".
15
16
diff --git a/utils/AMS/hacking/amsinfo.c b/utils/AMS/hacking/amsinfo.c
new file mode 100644
index 0000000000..0ebfb94caf
--- /dev/null
+++ b/utils/AMS/hacking/amsinfo.c
@@ -0,0 +1,175 @@
1/*
2
3amsinfo - a tool for examining AMS firmware files
4
5Copyright (C) Dave Chapman 2007
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program; if not, write to the Free Software
19Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
20
21*/
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <stdint.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <fcntl.h>
29#include <unistd.h>
30
31
32/* Win32 compatibility */
33#ifndef O_BINARY
34#define O_BINARY 0
35#endif
36
37
38#define PAD_TO_BOUNDARY(x) ((x) + 0x1ff) & ~0x1ff;
39
40
41static off_t filesize(int fd) {
42 struct stat buf;
43
44 if (fstat(fd,&buf) < 0) {
45 perror("[ERR] Checking filesize of input file");
46 return -1;
47 } else {
48 return(buf.st_size);
49 }
50}
51
52static uint32_t get_uint32le(unsigned char* p)
53{
54 return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
55}
56
57static uint16_t get_uint16le(unsigned char* p)
58{
59 return p[0] | (p[1] << 8);
60}
61
62static int calc_checksum(unsigned char* buf, int n)
63{
64 int sum = 0;
65 int i;
66
67 for (i=0;i<n;i+=4)
68 sum += get_uint32le(buf + 0x400 + i);
69
70 return sum;
71}
72
73
74static void dump_header(unsigned char* buf, int i)
75{
76 printf("0x%08x:\n",i);
77 printf(" HEADER: 0x%08x\n",i);;
78 printf(" FirmwareHeaderIndex: 0x%08x\n",get_uint32le(&buf[i]));
79 printf(" FirmwareChecksum: 0x%08x\n",get_uint32le(&buf[i+0x04]));
80 printf(" CodeBlockSizeMultiplier: 0x%08x\n",get_uint32le(&buf[i+0x08]));
81 printf(" FirmwareSize: 0x%08x\n",get_uint32le(&buf[i+0x0c]));
82 printf(" Unknown1: 0x%08x\n",get_uint32le(&buf[i+0x10]));
83 printf(" ModelID: 0x%04x\n",get_uint16le(&buf[i+0x14]));
84 printf(" Unknown2: 0x%04x\n",get_uint16le(&buf[i+0x16]));
85}
86
87static int dump_lib(unsigned char* buf, int i)
88{
89 int export_count;
90 int size;
91 int unknown1;
92 int baseaddr, endaddr;
93
94 baseaddr = get_uint32le(&buf[i+0x04]);
95 endaddr = get_uint32le(&buf[i+0x08]);
96 size = get_uint32le(&buf[i+0x0c]);
97 unknown1 = get_uint32le(&buf[i+0x10]);
98 export_count = get_uint32le(&buf[i+0x14]);
99
100 printf("0x%08x: \"%s\" 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",i, buf + i + get_uint32le(&buf[i]),baseaddr,endaddr,size,unknown1,export_count);
101
102#if 0
103 if (export_count > 1) {
104 for (j=0;j<export_count;j++) {
105 printf(" Exports[%02d]: 0x%08x\n",j,get_uint32le(&buf[i+0x18+4*j]));
106 }
107 }
108#endif
109 return PAD_TO_BOUNDARY(size);
110}
111
112int main(int argc, char* argv[])
113{
114 int fd;
115 off_t len;
116 int n;
117 unsigned char* buf;
118 int firmware_size;
119 int i;
120
121 if (argc != 2) {
122 fprintf(stderr,"USAGE: amsinfo firmware.bin\n");
123 return 1;
124 }
125
126 fd = open(argv[1],O_RDONLY|O_BINARY);
127
128 if ((len = filesize(fd)) < 0)
129 return 1;
130
131 if ((buf = malloc(len)) == NULL) {
132 fprintf(stderr,"[ERR] Could not allocate buffer for input file (%d bytes)\n",(int)len);
133 return 1;
134 }
135
136 n = read(fd, buf, len);
137
138 if (n != len) {
139 fprintf(stderr,"[ERR] Could not read file\n");
140 return 1;
141 }
142
143 close(fd);
144
145 /* Now we dump the firmware structure */
146
147 dump_header(buf,0); /* First copy of header block */
148// dump_header(buf,0x200); /* Second copy of header block */
149
150 firmware_size = get_uint32le(&buf[0x0c]);
151
152 printf("Calculated firmware checksum: 0x%08x\n",calc_checksum(buf,firmware_size));
153
154 /* Round size up to next multiple of 0x200 */
155
156 firmware_size = PAD_TO_BOUNDARY(firmware_size);
157
158 i = firmware_size + 0x400;
159
160 printf("LIBRARY BLOCKS:\n");
161 printf("Offset Name BaseAddr EndAddr BlockSize Unknown1 EntryCount\n");
162
163 while (get_uint32le(&buf[i]) != 0xffffffff)
164 {
165 i += dump_lib(buf,i);
166
167 while (get_uint32le(&buf[i]) == 0xefbeadde)
168 i+=4;
169 }
170
171 printf("0x%08x: PADDING BLOCK\n",i);
172
173 return 0;
174
175}
diff --git a/utils/AMS/hacking/mkamsboot.c b/utils/AMS/hacking/mkamsboot.c
new file mode 100644
index 0000000000..a6e4e01532
--- /dev/null
+++ b/utils/AMS/hacking/mkamsboot.c
@@ -0,0 +1,289 @@
1/*
2
3mkamsboot.c - a tool for merging bootloader code into an Sansa V2
4 (AMS) firmware file
5
6Copyright (C) Dave Chapman 2008
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2 of the License, or
11(at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
21
22*/
23
24
25/*
26
27Insert a Rockbox bootloader into an AMS original firmware file.
28
29The first instruction in an AMS firmware file is always of the form:
30
31 ldr pc, [pc, #xxx]
32
33where [pc, #xxx] contains the entry point of the firmware - e.g. 0x00000138
34
35mkamsboot appends the Rockbox bootloader to the end of the original
36firmware block in the firmware file and shifts the remaining contents of the firmware file to make space for it.
37
38It also replaces the contents of [pc, #xxx] with the entry point of
39our bootloader - i.e. the length of the original firmware block plus 4
40bytes.
41
42It then stores the original entry point from [pc, #xxx] in the first
43four bytes of the Rockbox bootloader image, which is used by the
44bootloader to dual-boot.
45
46Finally, mkamsboot corrects the length and checksum in the main
47firmware headers (both copies), creating a new legal firmware file
48which can be installed on the device.
49
50*/
51
52
53#include <stdio.h>
54#include <stdlib.h>
55#include <stdint.h>
56#include <sys/types.h>
57#include <sys/stat.h>
58#include <fcntl.h>
59#include <unistd.h>
60#include <string.h>
61
62
63/* Win32 compatibility */
64#ifndef O_BINARY
65#define O_BINARY 0
66#endif
67
68
69#define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff)
70
71
72static off_t filesize(int fd) {
73 struct stat buf;
74
75 if (fstat(fd,&buf) < 0) {
76 perror("[ERR] Checking filesize of input file");
77 return -1;
78 } else {
79 return(buf.st_size);
80 }
81}
82
83static uint32_t get_uint32le(unsigned char* p)
84{
85 return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
86}
87
88static void put_uint32le(unsigned char* p, uint32_t x)
89{
90 p[0] = x & 0xff;
91 p[1] = (x >> 8) & 0xff;
92 p[2] = (x >> 16) & 0xff;
93 p[3] = (x >> 24) & 0xff;
94}
95
96static int calc_checksum(unsigned char* buf, uint32_t n)
97{
98 uint32_t sum = 0;
99 uint32_t i;
100
101 for (i=0;i<n;i+=4)
102 sum += get_uint32le(buf + i);
103
104 return sum;
105}
106
107void usage(void)
108{
109 printf("Usage: mkamsboot <firmware file> <boot file> <output file>\n");
110
111 exit(1);
112}
113
114int main(int argc, char* argv[])
115{
116 char *infile, *bootfile, *outfile;
117 int fdin, fdboot,fdout;
118 off_t len;
119 uint32_t n;
120 unsigned char* buf;
121 uint32_t ldr;
122 uint32_t origoffset;
123 uint32_t firmware_size;
124 uint32_t firmware_paddedsize;
125 uint32_t bootloader_size;
126 uint32_t new_paddedsize;
127 uint32_t sum,filesum;
128 uint32_t new_length;
129 uint32_t i;
130
131 if(argc != 4) {
132 usage();
133 }
134
135 infile = argv[1];
136 bootfile = argv[2];
137 outfile = argv[3];
138
139 /* Open the bootloader file */
140 fdboot = open(bootfile, O_RDONLY|O_BINARY);
141 if (fdboot < 0)
142 {
143 fprintf(stderr,"[ERR] Could not open %s for reading\n",bootfile);
144 return 1;
145 }
146
147 bootloader_size = filesize(fdboot);
148
149
150 /* Open the firmware file */
151 fdin = open(infile,O_RDONLY|O_BINARY);
152
153 if (fdin < 0) {
154 fprintf(stderr,"[ERR] Could not open %s for reading\n",infile);
155 return 1;
156 }
157
158 if ((len = filesize(fdin)) < 0)
159 return 1;
160
161 /* We will need no more memory than the total size plus the bootloader size
162 padded to a boundary */
163 if ((buf = malloc(len + PAD_TO_BOUNDARY(bootloader_size))) == NULL) {
164 fprintf(stderr,"[ERR] Could not allocate buffer for input file (%d bytes)\n",(int)len);
165 return 1;
166 }
167
168 n = read(fdin, buf, len);
169
170 if (n != (uint32_t)len) {
171 fprintf(stderr,"[ERR] Could not read firmware file\n");
172 return 1;
173 }
174
175 close(fdin);
176
177 /* Get the firmware size */
178 firmware_size = get_uint32le(&buf[0x0c]);
179
180 /* Round size up to next multiple of 0x200 */
181
182 firmware_paddedsize = PAD_TO_BOUNDARY(firmware_size);
183
184 /* Total new size */
185 new_paddedsize = PAD_TO_BOUNDARY(firmware_size + bootloader_size);
186
187 /* Total new size of firmware file */
188 new_length = len + (new_paddedsize - firmware_paddedsize);
189
190 fprintf(stderr,"Original firmware size - 0x%08x\n",firmware_size);
191 fprintf(stderr,"Padded firmware size - 0x%08x\n",firmware_paddedsize);
192 fprintf(stderr,"Bootloader size - 0x%08x\n",bootloader_size);
193 fprintf(stderr,"New padded size - 0x%08x\n",new_paddedsize);
194 fprintf(stderr,"Original total size of firmware - 0x%08x\n",(int)len);
195 fprintf(stderr,"New total size of firmware - 0x%08x\n",new_length);
196
197 if (firmware_paddedsize != new_paddedsize) {
198 /* Move everything after the firmare block "bootloader_size"
199 bytes forward to make room for the bootloader */
200
201 fprintf(stderr,"Calling memmove(buf + 0x%08x,buf + 0x%08x,0x%08x)\n",
202 0x400 + new_paddedsize,
203 0x400 + firmware_paddedsize,
204 (int)len - firmware_paddedsize);
205
206 memmove(buf + 0x400 + new_paddedsize,
207 buf + 0x400 + firmware_paddedsize,
208 len - firmware_paddedsize);
209 }
210
211 ldr = get_uint32le(&buf[0x400]);
212
213 if ((ldr & 0xfffff000) != 0xe59ff000) {
214 fprintf(stderr,"[ERR] Firmware file doesn't start with an \"ldr pc, [pc, #xx]\" instruction.\n");
215 return 1;
216 }
217 origoffset = (ldr&0xfff) + 8;
218
219 printf("original firmware entry point: 0x%08x\n",get_uint32le(buf + 0x400 + origoffset));
220 printf("New entry point: 0x%08x\n", firmware_size + 4);
221
222#if 0
223 /* Replace the "Product: Express" string with "Rockbox" */
224 i = 0x400 + firmware_size - 7;
225 while ((i > 0x400) && (memcmp(&buf[i],"Express",7)!=0))
226 i--;
227
228 i = (i + 3) & ~0x3;
229
230 if (i >= 0x400) {
231 printf("Replacing \"Express\" string at offset 0x%08x\n",i);
232 memcpy(&buf[i],"Rockbox",7);
233 } else {
234 printf("Could not find \"Express\" string to replace\n");
235 }
236#endif
237
238 n = read(fdboot, buf + 0x400 + firmware_size, bootloader_size);
239
240 if (n != bootloader_size) {
241 fprintf(stderr,"[ERR] Could not bootloader file\n");
242 return 1;
243 }
244 close(fdboot);
245
246 /* Replace first word of the bootloader with the original entry point */
247 put_uint32le(buf + 0x400 + firmware_size, get_uint32le(buf + 0x400 + origoffset));
248
249#if 1
250 put_uint32le(buf + 0x400 + origoffset, firmware_size + 4);
251#endif
252
253 /* Update checksum */
254 sum = calc_checksum(buf + 0x400,firmware_size + bootloader_size);
255
256 put_uint32le(&buf[0x04], sum);
257 put_uint32le(&buf[0x204], sum);
258
259 /* Update firmware block count */
260 put_uint32le(&buf[0x08], new_paddedsize / 0x200);
261 put_uint32le(&buf[0x208], new_paddedsize / 0x200);
262
263 /* Update firmware size */
264 put_uint32le(&buf[0x0c], firmware_size + bootloader_size);
265 put_uint32le(&buf[0x20c], firmware_size + bootloader_size);
266
267 /* Update the whole-file checksum */
268 filesum = 0;
269 for (i=0;i < new_length - 4; i+=4)
270 filesum += get_uint32le(&buf[i]);
271
272 put_uint32le(buf + new_length - 4, filesum);
273
274
275 /* Write the new firmware */
276 fdout = open(outfile, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
277
278 if (fdout < 0) {
279 fprintf(stderr,"[ERR] Could not open %s for writing\n",outfile);
280 return 1;
281 }
282
283 write(fdout, buf, new_length);
284
285 close(fdout);
286
287 return 0;
288
289}
diff --git a/utils/AMS/hacking/test.S b/utils/AMS/hacking/test.S
new file mode 100644
index 0000000000..52f54bdaf6
--- /dev/null
+++ b/utils/AMS/hacking/test.S
@@ -0,0 +1,11 @@
1
2/* This value is filled in by mkamsboot */
3originalentry: .word 0
4
5 /* A delay loop - just to prove we're running */
6 mov r1, #0x500000 /* Approximately 5 seconds */
7loop: subs r1, r1, #1
8 bne loop
9
10 /* Now branch back to the original firmware's entry point */
11 ldr pc, originalentry